Reputation: 1653
TLDR: How do I get a component to update immediately on a textarea onChange event in another component?
I'm building a small JSON formatter app in order to learn React. I have an InputArea
component to enter the JSON, and an OutputArea
component to display the formatted JSON, both within my App
component. I have a basic working example except that the formatted output is appearing one step behind it being entered into the textarea. I realise this is to do with setState
and the fact that the state transition is pending rather than immediate. I believe I can use an updater callback with setState
, or, as is recommended, use the componentDidUpdate
lifecycle method, but I can't get my head around what I need to do in componentDidUpdate
to get setState
to update?
The code is not great, I'm just trying to get it working for now.
class App extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: "Paste your JSON here.",
outputValue: "",
hasError: false,
errorValue: ""
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({ inputValue: event.target.value })
let parsed = ""
try {
parsed = JSON.parse(this.state.inputValue)
} catch (e) {
this.setState({
hasError: true,
errorValue: e
})
return
}
const formatted = JSON.stringify(parsed, null, 2)
this.setState({
outputValue: formatted,
hasError: false,
errorValue: ""
})
}
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Formatter-tat-tat</h1>
</header>
<InputArea
value={this.state.inputValue}
handleChange={this.handleChange}
/>
<OutputArea
hasError={this.state.hasError}
value={this.state.outputValue}
error={this.state.errorValue}
/>
</div>
)
}
}
export default class InputArea extends Component {
render() {
return (
<div className="input-area">
<form>
<label>
JSON:
<textarea
value={this.props.value}
onChange={this.props.handleChange}
/>
</label>
</form>
</div>
)
}
}
export default class OutputArea extends Component {
render() {
if (this.props.hasError) {
return (
<div className="output-area">
<p>Error: {this.props.error.toString()}</p>
</div>
)
}
return (
<div className="output-area">
<pre>{this.props.value}</pre>
</div>
)
}
}
Upvotes: 0
Views: 819
Reputation: 20152
You are one step before because exactly this code part:
1. this.setState({ inputValue: event.target.value })
2. let parsed = ""
3. try {
4. parsed = JSON.parse(this.state.inputValue)
5. }
...
Lets go through above lines:
Line 1: You are using setState to push your component into new state, but as you said in the question it is not immediately action. So the change is not here yet.
Line 4: You are using the old state value, because as I've described in line 1 the change of the state is not reflected in this.state yet.
The fix: In the line 4 use event.target.value
instead of out-dated this.state.inputValue
Upvotes: 1