1

While trying out ReactJs, I am facing the below design issue:

Below is a simple html where I am trying to simulate a chat app, I am currently building a list of online members and trying to work out the code to add new members to the list.

The below React app consists of the following class in their DOM hierarchy.

ChatApp > MemberList > Member

In the react class ChatApp, I am able to use setInterval to update the members collection and generate new DOM for a new member, but now I want to try the same from outside the ChatApp class.

So I have added a button 'Add Member' and would like to call a function on its click which would in turn add a new member to the list.

But, How should I get instance of the react ChatApp and its setState method from outside of the react app?

<html>

<head>

    <style>
        .onlineList{
            width:300px;
            height:500px;
            display:inline-block;
            overflow:auto;
            border:1px solid lightgray;
        }

        .onlineList a{
            display:inline-block;
            width:100%;
            padding:10px;
            box-sizing:border-box;
            text-decoration:none;
            color:gray;
            font-weight:bold;
            border-top:1px solid lightgray;
            border-bottom:1px solid lightgray;
        }
        .onlineList a:hover{
            color:black;
            background-color:lightgray;
        }

    </style>

    <script src="js/jquery-1.11.3.min.js"></script>
    <script src="js/react-with-addons.js"></script>
    <script src="js/JSXTransformer.js"></script>

    <script>
        function addNewMember(){
            // How can the setState in chatApp react class be called from here?
        }
    </script>

    </head>

<body>
    <button onClick="addNewMember()">Add New Member</button>
    <div id="chatApp"></div>
    <script type="text/jsx">
        var Member = React.createClass({
            render: function() {
                return <a href='#'>{this.props.memberInfo}</a>;
            }
        });

        var MemberList = React.createClass({
            render: function(){
                var membersDOM = [];
                for(var m=0; m < this.props.members.length;m++){
                    membersDOM.push(<Member memberInfo={this.props.members[m].name}/>);
                }
                return (
                    <div id='onlineList' className={'onlineList'}>{membersDOM}</div>
                )
            }
        });

        var ChatApp = React.createClass({
            getInitialState: function(){
                var members = {
                    members: []
                };
                for(var m=0;m<1;m++){
                    members.members.push({name: m + 1});
                };

/*              setInterval(function(){
                    members.members.push({name: members.members.length + 1 });
                    this.setState(members);
                }.bind(this),2000);
*/
                return members;
            },
            render: function(){
                return (
                    <div id='container'>
                        <MemberList members={this.state.members}/>
                    </div>
                )
            }
        })

        React.render(
            <ChatApp/>,
            document.getElementById('chatApp')
        );

    </script>
</body>


</html>
2
  • 1
    If you are using React, you should probably also use the Flux application architecture, it is designed to work with React's reactive rendering. Check out facebook.github.io/flux/docs/overview.html. Basically, you'd render your components based on data from a store. When Add Member gets clicked, you dispatch an action to update the store, which then updates the react component. Commented Jul 11, 2015 at 8:51
  • @Hans, Oh ok, I will have a look at that. Commented Jul 11, 2015 at 9:07

1 Answer 1

1

You're never supposed to set a component's state outside of the component.

A React app should be set up as a hierarchy of components, with the top-level component managing the state of its children. So if you want this functionality outside of ChatApp, you need to move the button into a parent component, along with the state you want to modify and the addNewMember handler. You would then pass that state to ChatApp as props, just like you currently do with MemberList.

That being said, there's no reason why you couldn't just include the button and addNewMember in ChatApp:

addNewMember: function (e) {
  e.preventDefault();

  var name = this.state.members.length + 1;
  var members = this.state.members;

  this.setState({
    members: members.concat([{name: name}])
  });
}, 

render: function(){ 
  return (
    <div>
      <button onClick={this.addNewMember}>Add New Member</button>
      <div id='container'>
        <MemberList members={this.state.members}/>
      </div>
    </div>
  ) 
}

Also, this isn't directly related to your question, but you shouldn't call setState in getInitialState. Its purpose is to simply instantiate the component's state, since its invoked once, before the component is rendered, and doesn't trigger a re-render like other lifecycle methods.

So in your example, you would return just an empty members array. Then, if you wanted to add members with a setInterval, setTimeout, or any async process, you would do so in componentDidMount. Using addNewMember from above, it would look something like this (make sure to account for e if you run it):

getInitialState: function () {
  return {
    members: []
  };
},

componentDidMount: function () {
  setInterval(this.addNewMember, 2000);
}

Anyway, I hope this helps. Feel free to ask me any questions. I'd be happy to go into more detail about structuring more complex React apps and best practices.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, for a detailed explanation. I wanted to try out the 'add' button external to the app because I was assuming a scenario of an already developed page having some dynamic section written with jquery, which gets updated on events/data generated from somewhere else in the page. Now how would the whole thing work if that section be replaced with a react component and with no/little changes made to event/data generating mechanism. This might not actually be practical but I was just wondering.
@codin I'm not sure I understand what you're asking. Please provide more detail...
Never mind the question, I believe I should focus on understanding react better right now. Thanks!
@codin check out this video by Pete Hunt on React. It really helped me when I first started: youtu.be/DgVS-zXgMTk

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.