反应。 setState 不更新子组件的状态

React. setState does not update state of subcomponents

这是我的组件结构: 汇率输入 汇率值输入 汇率对话框

状态 open 作为道具传递给 ExhangeRateDialog。但是当我 setStateExchangeRateInput 中打开为真时,它在 ExhangeRateDialog 中似乎没有改变。有人可以解释我做错了什么吗?

ExchangeRateInput

import { composeWithTracker, composeAll } from 'react-komposer';
import React from 'react';
import useContext from '../../../containers/useContext.jsx';
import ExchangeRateValueInput from './ExchangeRateValueInput.jsx';
import ExchangeRateDialog from '../dialogs/ExchangeRateDialog.jsx';

const composer = (props, onData) => {
  onData(null, {});
};

export class ExchangeRateInput extends React.Component { //Wrapper Component
  constructor(props) {
    super(props);
    this.state = {
      value: props.value || '',
      date: '',
      showDialog: props.showDialog || false,
    };
    this.onChange = this.onChange.bind(this);
  }

  onChange(event) {
    const value = event.target.value;

    this.setState({ value });
  }

  onOpenDialog() {
    let bool = true;
    this.setState({ showDialog: bool }, () => {
      console.log(this.state);
    });
  }

  render() {
    return (
      <div>
        <ExchangeRateValueInput onChange={this.onChange} openDialog={this.onOpenDialog.bind(this)} value={this.state.value} />
        <ExchangeRateDialog onChange={this.onChange} open={this.state.showDialog} />
      </div>
    );
  }
}

ExchangeRateInput.propTypes = {
  value: React.PropTypes.number,
  onChange: React.PropTypes.func,
  openExhangeRateDialog: React.PropTypes.func,
};

const ComposedExchangeRateInput = composeAll(
  composeWithTracker(composer),
  useContext()
)(ExchangeRateInput);

export default ExchangeRateInput;

ExchangeRateDialog

import React from 'react';
import FlatButton from 'material-ui/FlatButton';
import DatePicker from 'material-ui/DatePicker';
import Dialog from 'material-ui/Dialog';
import useContext from '../../../containers/useContext.jsx';

export class ExchangeRateDialog extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '',
      date: '',
      open: props.open || false,
    };
    this.onDialogConfirm = this.onDialogConfirm.bind(this);
    this.onDateChange = this.onDateChange.bind(this);
  }

  onDateChange(date) {
    const value = this.getFakeExchangeRate(); // Replace functionallity with Meteor-method
    setTimeout(() => {
      this.setState({ date, value });
    }, 1100);
  }

  onDialogConfirm() {
    this.props.onDialogConfirm({
      value: this.state.value,
      date: this.state.date,
    });
  }

  getFakeExchangeRate() {
    return Math.random(1, 15);
  }

  actions() {
    return [
      <FlatButton
        label="Cancel"
        secondary
        onTouchTap={this.props.onDialogCancel}
      />,
      <FlatButton
        label="Ok"
        primary
        onTouchTap={this.onDialogConfirm}
        disabled={!this.state.value}
      />,
    ];
  }

  render() {
    return (
      <div >
        <Dialog
          title="Get exchange rate from Riksbanken"
          modal={false}
          open={this.state.open}
          actions={this.actions()}
          onRequestClose={this.props.onDialogCancel}
        >
            Choose a date.
          <div className="layout horizontal">
            <div className="flex">
              <DatePicker
                hintText="No date selected"
                onChange={(event, date) => this.onDateChange(date)}
                maxDate={new Date()}
              />
            </div>
            <div className="flex">
              <h3>{this.state.value ? `Exchange rate: ${this.state.value}` : null}</h3>
            </div>
          </div>
        </Dialog>
      </div>
    );
  }
}

ExchangeRateDialog.propTypes = {
  value: React.PropTypes.number,
  date: React.PropTypes.string,
  open: React.PropTypes.bool,
  onChange: React.PropTypes.func,
  onDialogCancel: React.PropTypes.func,
  onDialogConfirm: React.PropTypes.func,
};

export default ExchangeRateDialog;

ExchangeRateValueInput

import React from 'react';
import TextField from 'material-ui/TextField';
import IconButton from 'material-ui/IconButton';
import useContext from '../../../containers/useContext.jsx';

export class ExchangeRateValueInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: props.value || '',
      errorMessage: '',
    };
    this.onValueChange = this.onValueChange.bind(this);
  }

  onValueChange(event) {
    const value = event.target.value;
    let errorMessage = '';
    let returnValue = value;

    const isNumber = !isNaN(value); // TODO: Improve validation

    if (!isNumber) {
      errorMessage = 'Must be a number';
      returnValue = '';
    }

    this.setState({
      value,
      errorMessage,
    }, () => {
      this.props.onChange(returnValue);
    });
  }

  onOpenDialog() {
    console.log('hej');
    console.log(this.props);
    this.props.onOpenDialog;
  }

  style = {
    height: 72,
  };

  render() {
    return (
      <div className="layout horizontal" style={this.style}>
        <div
          className=""
        >
          <TextField
            floatingLabelText="Value"
            onChange={this.onValueChange}
            errorText={this.state.errorMessage}
            value={this.state.value}
          />
        </div>
        <div
          className="layout center layout horizontal"
        >
          <IconButton
            className="flex"
            tooltip="Get from Riksbanken"
            onClick={() => this.props.openDialog()}
          >
            <i className="material-icons">search</i>
          </IconButton>
        </div>
      </div>
    );
  }
}

ExchangeRateValueInput.propTypes = {
  value: React.PropTypes.number,
  onChange: React.PropTypes.func,
  openDialog: React.PropTypes.func,
};

export default ExchangeRateValueInput;

您将 valueExchangeRateInput 传递到 ExchangeRateValueInput,但随后您将其存储在 ExchangeRateValueInput 的内部状态中。此时,它与 ExchangeRateInput 完全断开了连接。因此,当 ExchangeRateValueInput 更新其状态时,其 parent 对此一无所知。

您可以重写一些东西,以便所有状态都在根组件 (ExchangeRateInput) 中进行管理,并将该状态向下传递给 children,后者只引用 props,而不是它们自己的内部状态.然后,当 children 需要更新状态时,它们可以调用您定义的一些事件(例如 this.props.onChange 或其他)。 ExchangeRateInput 将处理这些事件,并相应地更新其状态。

或者,您需要研究使用通量模式(例如使用 Redux 之类的东西)。