Reputation: 5
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;
Upvotes: 0
Views: 1591
Reputation: 4561
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 (e.g. 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 (e.g. using something like Redux).
Upvotes: 1