Component state
The wrong way
Let's first try to simply add a code by updating the array of codes, and then log the updated codes. We expect the new code to appear below the third code.
function CodeListEditor() {
const codes = [{
id: 'code_1',
label: 'unhappy'
}, {
id: 'code_2',
label: 'happy',
}, {
id: 'code_3',
label: 'very happy'
}]
const handleClick = () => {
codes.push({ id: 'code_4', label: 'very unhappy' })
console.log('codes:', codes)
}
return (
<div>
<button onClick={handleClick} />
...
</div>
)
}
As you notice, it does not quite work: the codes seem to have been updated (there are now 4 codes in our array), but the UI stayed the same.
This is a key feature of React: the app will only be re-rendered when a setState
method is called somewhere. To use the setState
method, we will need to make some adjustments to our CodeListEditor
.
The right way
To use the setState
method aforementioned, we need to use a different syntax to describe the component:
import React from 'react'
class CodeListEditor extends React.Component {
render() {
const codes = ...
return (
<div>
<button onClick={handleClick} />
...
</div>
)
}
}
We've just copied the body from our component function to the body of another function: the render
method of a ES6 class which extends React.Component
.
For now, nothing has changed: the UI is still not updating when we try to add a code. But this syntax exposes some additional functionalities. Among them is the option to call the setState
method to tell the component something has changed and it should be re-rendered.
import React from 'react'
class CodeListEditor extends React.Component {
constructor() {
super()
//we initialize the state with the 3 codes
this.state = {
codes: [{
id: 'code_1',
label: 'unhappy'
}, {
id: 'code_2',
label: 'happy',
}, {
id: 'code_3',
label: 'very happy'
}]
}
this.addCode = () => {
const { codes } = this.state
const newId = `code_${codes.length+1}`
//Instead of updating the codes in place, with `codes.push`, we prefer to
//make a copy of the initial codes and append the new code at the end.
const newCodes = [...codes, { id: newId, label: 'very unhappy' }]
//we can now use `setState` to make this new array of codes part of the
//component state. The `setState` call will trigger a re-rendering of the
//component.
this.setState({
codes: newCodes
})
}
}
render() {
//codes should now be extracted from the component state
const { codes } = this.state
return (
<div>
{/* we use the `addCode` method defined in the constructor */}
<button onClick={this.addCode}>Add a code</button>
<div>
{
codes.map(({ id, label }) => <CodeEditor key={id} label={label} />)
}
</div>
</div>
)
}
}
We intitialize the state of the CodeListEditor
component with the 3 already created codes. We then define a addCode
function (see arrow functions) which:
- creates an
id
for the newly created code; - creates a copy of the initial codes with a new code at the end (see spread operator) ;
- calls
setState
to update the state of the component; thesetState
call will trigger a re-rendering of the component.
Now, when we click on the "Add a code" button, the UI updates accordingly.
Play with this pen.