简体   繁体   中英

React. setState does not update state of subcomponents

This is my component-structure: ExchangeRateInput ExhangeRateValueInput ExhangeRateDialog

The state open in is passed through to ExhangeRateDialog as a prop. But when i setState open to true in ExchangeRateInput it doesnt seem to change in ExhangeRateDialog . Can someone please explain what i am doing wrong.

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;

You're passing value from ExchangeRateInput to ExchangeRateValueInput , but then you're storing it in the internal state of ExchangeRateValueInput . At this point, its completely disconnected from ExchangeRateInput . So, when ExchangeRateValueInput updates its state, its parent knows nothing about it.

You could rewrite things so that all state is managed in the root component ( ExchangeRateInput ) and it passes that state down to the children who only reference props, rather than their own internal state. Then, when the children need to update state, they can call some event that you define (eg this.props.onChange or something). ExchangeRateInput would handle those events, and update its state accordingly.

Alternatively, you need to look into using a flux pattern (eg using something like Redux).

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