How to keep data separate with multiple root components?

2.4k views Asked by At

I'm building my whole site with React.js like this:

     React.render(
       <div id="mainWrapper">
         <MainHeader />
         <div id="contentWrapper" className="contentWrapper">
           <MainFlow data={example_data} />
           <AddElementWrapper data={activities} />
         </div>
       </div>,
       document.body
     );

However, calling setProperties on one of my data-holding components, namely MainFlow or AddElementWrapper, I get an error, stating that I should manipulate the data via their parents.

Uncaught Error: Invariant Violation: replaceProps(...): You called `setProps` or `replaceProps` on a component with a parent. This is an anti-pattern since props will get reactively updated when rendered. Instead, change the owner's `render` method to pass the correct value as props to the component where it is created.

First off: I thought that this would only be a problem if there is another owner for the component I want to manipulate. However, my components don't have React components as owners, just HTML elements as parents.

I could circumvent this problem by attaching ALL application data to a single root component. However, I'd like to keep certain sets of data separate. Is there a way?

3

There are 3 answers

0
Dhruva Sagar On BEST ANSWER

You should define a Wrapper component and maintain the state of data there and simply invoke setState when data changes from the outside world.

Wrapper = React.createClass({
getInitialState: function() {
    return {example_data: example_data, activities: activities};
},
refresh: function() {
    this.setState({example_data: this.state.example_data, activities: this.state.activities});
},
render: function() {
    return (
        <div id="mainWrapper">
            <MainHeader/>
            <div id="contentWrapper" className="contentWrapper">
                <MainFlow data={this.state.example_data} />
                <AddElementWrapper data={this.state.activities} />
            </div>
        </div>
    );
  }
});

React.render(<Wrapper/>);
3
Dimitar Dimitrov On

You can copy the props to the state of the child component and then change the state.

In MainFlow and AddElementWrapper components you can use:

 getInitialState: function() {
  return {activities: this.props.data};
 },

Then you can use this.state.activities and modify it with setState({activities: yourObject })

1
Bactisme On

The idea will be to have React Components that explicitly listen to data changes. For that you will setup callback at your component mount.

I would suggest to look for two things : either Backbone+React (and the BackboneReact mixin) or Flux+React.

The flux way is to have a global events system about data availability (the dispatcher), and the the backbone way would be to register to a particular Collection events.

This code is directly from the BackboneMixin from the TodoMVC React-backbone project:

componentDidMount: function() {
  // Whenever there may be a change in the Backbone data, trigger a
  // reconcile.
  this.getBackboneCollections().forEach(function(collection) {
    // explicitly bind `null` to `forceUpdate`, as it demands a callback and
    // React validates that it's a function. `collection` events passes
    // additional arguments that are not functions
    collection.on('add remove change', this.forceUpdate.bind(this, null));
  }, this);
},

More :