简体   繁体   中英

Share parent state between child components' states?

Learning React, I'm building a simple Calculator. Components model is:

- Calculator
  - Sliders (input[ref=cars])
  - Dashboard (p {this.state.cars})

Sliders is component with set of inputs, that should be transfered to Dashboard via parent Calculator state.

My goal is to make state value in Dashboard change on Sliders s input component change.

var Calculator = React.createClass({
  getInitialState: function() {
    return {
      cars: 0
    };
  },
  render: function () {
    return (
      <div className="calculator">
        <Sliders
          cars={this.state.cars}
        />
        <Dashboard
          cars={this.state.cars}
        />
      </div>
    )
  }
});

var Dashboard = React.createClass({
  getInitialState:function () {
    return {
      cars: this.props.cars
    }
  },
  render: function () {
    return (
      <div>
        <h1>Dashboard</h1>
        <p>Cars: {this.state.cars}</p>
      </div>
    )
  }
})

var Sliders = React.createClass({
  getInitialState: function() {
    return {
      cars: this.props.cars
    };
  },
  handleInput: function () {
    var state = {
      cars: this.refs.cars.value
    }
    this.setState(state);
  },
  render: function () {
    return (
      <div className="sliders">
        <input type="text" ref="cars" onChange={this.handleInput} value={this.state.cars}/>
      </div>
    )
  }
})

As I've got, handleInput() is trigged, so state for Sliders is set. However, it's NOT "transfered" to parent Calculator 's state as 'cars'. So, Calculator 's state is not updated => Dashboard state is not udpated too.

How to share Calculator's parent state between Sliders and Dashboard?

I know, there are plenty of similar questions on SO, however it's hard to find the specific helpful case, especially when you're complete noob in React. So, sorry for duplicating.

Components don't share state. The state of one component can become the props of another component though.

In your case, cars should be the state of Calculator, and Slide and Dashboard should only retrieve cars as props. If the input changes, Sliders should notify Calculator about the change so that it can update its state and rerender (which causes Sliders and Calculator to update as well):

var Calculator = React.createClass({
  getInitialState: function() {
    return {
      cars: 0
    };
  },

  onUpdate: function (cars) {
    this.setState({cars});
  },

  render: function () {
    return (
      <div className="calculator">
        <Sliders
          cars={this.state.cars}
          onUpdate={this.onUpdate}
        />
        <Dashboard
          cars={this.state.cars}
        />
      </div>
    )
  }
});

var Dashboard = React.createClass({
  render: function () {
    return (
      <div>
        <h1>Dashboard</h1>
        <p>Cars: {this.props.cars}</p>
      </div>
    )
  }
})

var Sliders = React.createClass({
  handleInput: function (event) {
    this.props.onUpdate(event.target.value);
  },

  render: function () {
    return (
      <div className="sliders">
        <input type="text" ref="cars" onChange={this.handleInput} value={this.props.cars}/>
      </div>
    )
  }
})

As few components as possible should have state and state should be as high up in the tree as possible. See also Props in getInitialState Is an Anti-Pattern

Pass parent as a prop to slider, and you wont need cars as a state in sliders.

This is one way of doing it, but FelixKing's answer is better.

var Calculator = React.createClass({
  getInitialState: function() {
    return {
      cars: 0
    };
  },
  handleInput: function () {
    var state = {
      cars: this.refs.cars.value
    }
    this.setState(state);
  },
  render: function () {
    return (
      <div className="calculator">
        <Sliders
          cars={this.state.cars} parent={this}
        />
        <Dashboard
          cars={this.state.cars}
        />
      </div>
    )
  }
});

var Dashboard = React.createClass({
  getInitialState:function () {
    return {
      cars: this.props.cars
    }
  },
  render: function () {
    return (
      <div>
        <h1>Dashboard</h1>
        <p>Cars: {this.state.cars}</p>
      </div>
    )
  }
})

var Sliders = React.createClass({
  getInitialState: function() {
    return {};
  },
  handleInput: function (value) {
    this.props.parent.handleInput(this.refs.cars.value);
  },

  render: function () {
    return (
      <div className="sliders">
        <input type="text" ref="cars" onChange={this.handleInput} value={this.props.cars}/>
      </div>
    )
  }
})

In React you want to have one or several "smart" components, that handle state and "dumb" components. The "smart" components transfer the state down to the "dumb" component. The also inject event handlers, like in your case a handler for changing the amount of cars. Your calculator is your smart component. It should also have a function to handle the state change.

handleCarsChange: function( newAmount ) {
  this.setState({ cars: newAmount });
},

Also don't make your initial state from props ( more Information ). Props are made to change, use them directly in your render function like so:

var Dashboard = React.createClass({
  render: function () {
    return (
      <div>
        <h1>Dashboard</h1>
        <p>Cars: {this.props.cars}</p>
      </div>
    )
  }
});

Or with the React 14 component function syntax:

var Dashboard = function(props) {
    return (
      <div>
        <h1>Dashboard</h1>
        <p>Cars: {props.cars}</p>
      </div>
    );
};

Last but not least give the Sliders component a handler function to deal with the state change:

<Sliders
  cars={this.state.cars}
  handleCarsChange={this.handleCarsChange}
/>

And change the handleInput function in the Slider component:

handleInput: function(value) {
  this.props.handleCarsChange(this.refs.cars.value);
},

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM