Home Ask Login Register

Developers Planet

Your answer is one click away!

Naren February 2016

React: Using parent to pass data between sibling components

I need to render a widget which takes in settings from a SettingsPanel and passes them to a LayoutPanel (which would then re-render itself based on the updated settings). I can't seem to figure a 'clean' way to do this. Here's what I have so far:

Widget.js

class Widget extends Component {
    handleSettingsChange() {
        //need to let layout know of change
    }

    render() {
        <div>
            <SettingsPanel
                onSettingsChange={handleSettingsChange}
                initialSettings={this.props.settings}
            />
            <Layout
                data={ this.props.data}
            />
        </div>
    }
}

App.js

const settings = {
    hideImages: true,
    itemsPerPage: 5
}
<Widget settings={ settings } data={ data } />

My first thought was that, within Widget, I could do

constructor() {
    super()
    this.setState({ settings: this.props.settings });
}
handleSettingsChange(data) {
    //Assuming data is of the the form {changedProperty: value}
    this.setState({ settings: Object.assign({}, this.state.settings, data) });
}
render() {
    <div>
        <SettingsPanel
            onSettingsChange={handleSettingsChange}
            initialSettings={this.props.settings}
        />
        <Layout
            data={ this.props.data}
            settings={ this.state.settings }
        />
    </div>
}

This way the parent is a dump message broker, and doesn't not have to know the specifics of what specific settings are being change. But of course, this doesn't work because, a. I'm told setting state from props is an anti-pattern b. The constructor doesn't have access to this.props anyway, and doing it else-where (componentDidMount?) feels wrong.

My second thought was to go to setProps to set props on children fro

Answers


lukewestby February 2016

It's not necessarily true that setting state from props is an anti-pattern. It seems completely reasonable to initialize a component's state from props in the constructor. Doing so doesn't degrade testability or reliability much if at all.

You can access props in the constructor as they are passed as the first parameter. You'll need to call super first, but then you can do whatever you'd like:

class Widget extends React.Component {
  constructor(props) {
    super(props);
    this.setState({ settings: props.settings });
  }
}

Then you can continue down the path you were originally headed. Another slightly more complex option would be to use a state container like Redux to handle state management and transitions for you, but only do this if you don't mind the additional learning and complexity of adding more to your app setup.


Naren February 2016

While lukewestby's answer is correct in that it solves the problem in the original question, after further reflection I realized that that was the wrong question to ask - i.e., it was an architecture issue which is why I was having trouble with the implementation.

In this case the data-flow is [data]-> App -> Widget -> Children. "App" fetches data, so App owns it. Widget should not be able to to modify, handle changes to data it doesn't own. Hence, the callback needs to be passed to the grandparent (App), not the parent (Widget). Here's what I ended up doing.

App.js

 constructor() {
   this.state = { settings: { hideImages: true } };
 }
 handleSettingsChange(data) {
   this.setState({ settings: data });
 }
 render() {
   <Widget settings={ this.state.settings } 
      onSettingsChange={ this.handleSettingsChange }
    />
} 

Widget.js

handleDisplaySettingsChange(data) {
    this.props.onSettingsChange(data);
}
render() {
    return (
        <div>
            <SettingsPanel
                onSettingsChange={ this.handleDisplaySettingsChange }
            />
            <Layout
                {...this.props.settings }
            />
        </div>
    );
}

The advantage of doing it this way is that there is a single source of truth for settings, which is maintained by the owner (App). In the original suggestion, if on a later fetch by App the settings had changed the inner components wouldn't receive it (which explains why the React docs claim setting state from props is an anti-pattern)

Post Status

Asked in February 2016
Viewed 3,364 times
Voted 5
Answered 2 times

Search




Leave an answer


Quote of the day: live life