反应。 setState 不更新子组件的状态
React. setState does not update state of subcomponents
这是我的组件结构:
汇率输入
汇率值输入
汇率对话框
状态 open 作为道具传递给 ExhangeRateDialog。但是当我 setState 在 ExchangeRateInput 中打开为真时,它在 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;
您将 value
从 ExchangeRateInput
传递到 ExchangeRateValueInput
,但随后您将其存储在 ExchangeRateValueInput
的内部状态中。此时,它与 ExchangeRateInput
完全断开了连接。因此,当 ExchangeRateValueInput
更新其状态时,其 parent 对此一无所知。
您可以重写一些东西,以便所有状态都在根组件 (ExchangeRateInput
) 中进行管理,并将该状态向下传递给 children,后者只引用 props,而不是它们自己的内部状态.然后,当 children 需要更新状态时,它们可以调用您定义的一些事件(例如 this.props.onChange
或其他)。 ExchangeRateInput
将处理这些事件,并相应地更新其状态。
或者,您需要研究使用通量模式(例如使用 Redux 之类的东西)。
这是我的组件结构: 汇率输入 汇率值输入 汇率对话框
状态 open 作为道具传递给 ExhangeRateDialog。但是当我 setState 在 ExchangeRateInput 中打开为真时,它在 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;
您将 value
从 ExchangeRateInput
传递到 ExchangeRateValueInput
,但随后您将其存储在 ExchangeRateValueInput
的内部状态中。此时,它与 ExchangeRateInput
完全断开了连接。因此,当 ExchangeRateValueInput
更新其状态时,其 parent 对此一无所知。
您可以重写一些东西,以便所有状态都在根组件 (ExchangeRateInput
) 中进行管理,并将该状态向下传递给 children,后者只引用 props,而不是它们自己的内部状态.然后,当 children 需要更新状态时,它们可以调用您定义的一些事件(例如 this.props.onChange
或其他)。 ExchangeRateInput
将处理这些事件,并相应地更新其状态。
或者,您需要研究使用通量模式(例如使用 Redux 之类的东西)。