Confusion about *state* sharing between components in Game of Life

OK, I’ve been beating my head against a wall for two days now, time to reach out.

React is new to me. I think I’m realizing that my problem is that I started out with the assumption that state was universal. Now I’m understanding that each component has its own state that isn’t automatically shared. For example, when look at state in the master App element, it has a size property and so does its child Grid. However, handleStart() does not see that property. (I’m going to have to go scratch and completely rework my assumptions about the point of state.)

That is not my problem. My problem is that whatever state exists in handleStart() it apparently has no affect on the screen, leading me to think that whatever state is affecting the cells, it is not the same. How do I connect these two? I assume it has something to do with binding or passing around this, but so far it is too cunning to be understood, for me at least.

The logic seems sound as the iteration causes the change I would expect to see. It just never affects the view.

https://codepen.io/ksjazzguitar/pen/rmwOqr?editors=0110

Thanks,
Kevin

React is a hell of a thing to get your head around, for sure.

I don’t have time to dive deeply into your code, but I might have some helpful tips. First, you’re correct in that state is not shared between components. There are some quirks regarding this, so I need to give a little spiel. One of the major tenets of React is that the components which make up your view should be stateless, in your case, Cell and possibly Board. The other major tenet is that application state should flow from a single authority, meaning you ought to have one component that keeps track of state and passes changes down to child components.

To answer your question directly, the way these components communicate is through props, and you should pass as much information in this way as you can. So, instead of having your Cell components keeping track of their own state, the React way is to have the Grid (or possibly an even higher level component like App) keep track of which Cell should be on or off. The practical reason for this is that React keeps track of which components need to change by 1) re-rendering all components whose state has changed (unless prevented by the developer), and 2) calculating whether a state change affects any stateless child components and only re-rendering those who will receive new data. By keeping your data as centralized as possible, you allow React to make the most efficient changes to your view.

So, while I haven’t tested your code, the reason you’re not seeing the changes you expect could be because you’re letting too many components manage their own state and React doesn’t know when to update your Cells.

The this binding you’ve heard about is a trapping of using ES6 classes for writing components. Basically, you need to keep a reference to the component which defines the method because that’s how child components communicate data back up the hierarchy. It has nothing to do with React in particular, but React developers need to be conscious of it due to the structure of React apps.

I hope that helps at all. I’m pretty exhausted today, but I can try to answer any other questions you have.

Hey Kevin!

You may have heard this before, but React is designed to have state passed down from parent to children components as props. In order to have children update the state of a parent you need to pass down a function that does this. Libraries like Flux and Redux are popular because they allow much less tedious data transfer by centralizing state to a ‘store’.

Without diving in too deep I can see that you are trying to pass certain things from your App component that do not exist in your App component state, like ‘this.state.grid’ and ‘this.refs.grid.handleStart()’.

@PortableStick

Thanks. I understand conceptually what you’re saying, I just need to find a way to get my program to do it. So, I guess I need Grid (or maybe App) to keep the state and have Cell use that state to generate itself and have it’s click events refer back to Grid. Easier said than done. I understand it in theory, I’m just having a hard time putting it together.

@RadDog25

Yes, I’ve heard the state and prop passing thing before - but clearly I need to hear it again. I guess the missing key was that each component has it’s own state. But that makes sense since they have their own this.

Yes. this.state.grid is a mistake, leftover from a previous idea of how it should be. But this.refs.grid.handleStart() I think is right:

      <div className="container">
        <Controls {...this} onStart={() => this.refs.grid.handleStart()}/>
        <TitleBar />
        <Grid ref="grid" size={this.state.size}/>
      </div>

My understanding (possibly flawed) was that this was how one child component could call another child. In the Grid tag I write ref=“grid so the other child Controls can have a reference to call the function handleStart() in the Grid sibling. Maybe it was confusing because I had that leftover this.state.grid. Please let me know if I am wrong in my thinking.

Thankyou both for the input. I’m going to try to solve this with pure React, but if I later decide to go down that road, any suggestions between Flux and Redux?

Assuming you are comfortable with what @PortableStick and @RadDog25 adDog25 have said, particularly with regards to here are some comments, below are some comments I have after looking through your code (apologies in advance because there will most likely be overlaps of what has already been said).

  1. Going from the “top”, App does not have a state called grid but it’s getting passed into the Grid component as this.state.grid
  2. Following from above, it is worth noting that the states in a component can be manipulated by that component only. They can be passed to another component as props—but the component that receives these states as props cannot modify these states
  3. One of the most important implications of the second point is that props should not be used to set states (there are probably exceptions). Using your current code as an example, let’s suppose you implement a feature to change the size of the board later; you will find that it won’t be possible to change the size of the board at all because the constructor is only called once when the component is mounted
  4. The problem pointed out in point 3 is, I think, the reason that one-way data flow is emphasised in React development
  5. One way to restructure your current code could be as follows (it doesn’t have to be exactly this way, you can also put header and footer inside GameController; or have all the states in App, get rid of GameController and have Grid and Controls directly in App—I think this is more managable and, some of the things or even the structure can be used for later projects):
    • App—wraps all components and is passed onto ReactDOM.render
      • Header
      • GameController—where all states live
        • Grid—receives the relevant states as props for rendering
        • Controls—buttons and text boxes for triggering changes
      • Footer
  6. If we go with the structure above, you don’t need to specify the consts at the beginning of the document—you can simply have them initialised as states in the constructor function of <GameController>
  7. As a example, if <GameController> holds this.state.board, which is an array that contains all the information of the current generation of the board—you can simply do all the calculation inside <GameController> when setInterval triggers it. Since <Grid> would be wired to receive this.state.board, once you have done the calculation of the next generation in a function inside <GameController> and updated this.state.board with this.setState(), anything inside <Grid> that requires this.state.board will be automatically updated
  8. Passing events from a component is also a little weird at first. If you haven’t done the smaller React projects before the Game of Life project, they are highly recommended. A quick and dirty code example of how you have the Iterate button set up at the moment would be this (I also found it helpful at the beginning to map it out like this because it’s really difficult to get used)—code at the end of the post!
  9. It is worth noting that a lot of the logic can be done in the render() function of a component that is receiving states from a parent component
  10. It may be a bit of a distraction—but if you find it difficult to manage on CodePen, I highly recommend create-react-app and code locally, it is packaged with Webpack and Babel. I find that being able to look at different parts of the code at the same time with split window is a blessing, particular in a React app when you are making changes in a component and its parent/child

It looks like I was a bit late and your response popped up half way through when I was typing this. I hope this helps regardless! Also, apologies in advance if there are any mistakes (I’ve started learning React just a little over a month ago); if something sounds funny it’s probably because I’ve gotten it wrong and please do point it out! :slight_smile:

EDIT: This tutorial is highly recommended—even if you are not going to actually code it, it has good guidelines about various aspects of React app development and, in particular, a section on where states should live.

class Control extends React.Component {
    constructor(props) {
        ...
        this.handleButtonClick = this.handleButtonClick.bind(this);
    }

    // ===2=== onControlButtonClick is passed into this component from <Grid>
    // It is worth noting that information of the event can be passed back to <Grid> in this manner
    // An example would be a button click in a text box, where you can pass event.which || event.key back to <Grid>
    handleButtonClick (event) {
        this.props.onControlButtonClick();
    }

    render() {
        // ===1=== Entry point of a click event, triggers handleButtonClick() in this component
        return(
            <button onClick(this.handleButtonClick)>Iterate</button>
        );
    }
}
class App extends React.Component {
    constructor(props) {
        ...
        this.handleControlButtonClick = this.handleControlButtonClick.bind(this);
    }

    // ===4=== You finally get to do what you want to do! Well, almost.
    handleControlButtonClick() {
        // Start iterations
        // Set relevant states that keeps track of the board and the number of iterations
    }

    ...

    // ===3=== When <Control> receives a click event, it comes back here, which then calls for handleControlButtonClick()
    render() {
        ...
        <Controls onControlButtonClick={this.handleControlButtonClick}>
    }
}
1 Like

Kevin,

Reading up on Facebook’s documentation (https://facebook.github.io/react/docs/refs-and-the-dom.html) it looks like you may be correct in that using refs is one way to modify children, but their wording suggests that this should only be used sparingly. I can tell you that I’ve never used it before and I have created a number of React apps by this point.

I cooked up this simple demo to show one pattern that has worked for me:

I haven’t used Flux before but I have used Redux for a few of my larger projects and I liked the structure that it gave my code. It is kind of a big thing to setup though so for smaller apps I think that plain React is fine.

Thank you for all the well thought out responses. It all makes sense. I may just start from scratch (canibalizing logic here and there) to make sure it all works out right and to make sure that it’s organized correctly.

@honmanyau

Just to make sure I understand this:

As a example, if GameController holds this.state.board, which is an array that contains all the information of the current generation of the board—you can simply do all the calculation inside GameController when setInterval triggers it. Since Grid would be wired to receive this.state.board, once you have done the calculation of the next generation in a function inside GameController and updated this.state.board with this.setState(), anything inside that requires this.state.board will be automatically updated

I just want to make sure I understand, this “wiring” is passing the state.board to Grid, but in Grid it will be props.board. That is the “wiring”? And if everything is set up properly, when I modify the state in GameController, “magically” the prop.grid will be updated so the view will be “magically” updated? That’s how I understand it, but I just want to be sure since I was so far off on my understanding of state.

I’m glad that it makes sense! I also wanted to see if I can actually put things down in words, too, so it’s win-win! :slight_smile:

Many apologies for not having elaborated on the magical wiring bit earlier (magic is probably the best description, too). You are absolutely correct about the wiring that I mentioned! I expanded the code that I posted earlier a little and made a small demo on CodePen, with comments showing the flow of events and data as before (you can search for /* ===num=== */).

I forgot to mention earlier that it’s recommended to keep as few states as possible. If something can be derived from a state, it’s more than likely that you don’t need to keep a separate state for it.

EDIT: Oh! And when I started it always felt like a lot of code to get something small done—but the benefits do really come in for larger projects, particular if you keep writing highly reusable component.

I hope that helps!

1 Like

@honmanyau

…—but the benefits do really come in for larger projects…

Yeah, I get that. And I like the modular/component kind of thinking. I just wish working with state was not so Byzantine.

it looks like you may be correct in that using refs is one way to modify children, but their [Facebook’s] wording suggests that this should only be used sparingly

@RadDog25

OK. Thank you for the example. That shows a child calling a function in the parent. That’s a good review, but it wasn’t really what I was trying to do with the ref - I was trying to call the function of a child from a sibling child. Am I correct that the “correct” way to do that is that child one calls a function in the parent that changes the state (perhaps some boolean property) of the parent that in tern affects the props in child two and compnentWillReceiveProps will fire and from there I can test for that prop and run that child’s function? Is that the correct way to think?

Hey Kevin,

So basically your issue is that you want the ‘handleStart’ function to fire when the user clicks the ‘iterate’ button right?

I think that your main issue is that you are storing too much state in you children components. A straightforward way to avoid this is to just store every piece of state in your top level App component. This way you have just a single source of truth and have a lot less to think about. So for example if you moved your grid state to your App component you could use a similar pattern to the little example I wrote.

1 Like

Everything’s going much more smoothly now that I’m using state properly and I have things organized properly.

Thanks for the help,

1 Like