Vladimir Kutsev
Vladimir Kutsev

Reputation: 19

React state only update after second time being executed

I had this problem for a while and can't find a solution.

I'm trying to change the state to complex data.

const [state, setState] = useState(null)

function Save(){
    if(state){
        setState((prev)=>[...prev, [p1, p2, p3, p4]])
    }else{
        setState([[p1, p2, p3, p4]])
    }
}

First when I call the Save() function the state is changed correctly

Array [
  Array [
    1,
    2,
    3,
    4
  ],
]

But at the second run, I get the same state

Array [
  Array [
    1,
    2,
    3,
    4
  ],
]

After that everything works correctly fine and the state is added

Array [
  Array [
    1,
    2,
    3,
    4
  ],
  Array [
    5,
    6,
    7,
    8
  ],
  ....
]

here is the full code

export default function ModalForm({close}) {
    const { state, setState } = useStateContext()
    const [name, setName] = useState('')

    const [p1, setP1] = useState('')
    const [p2, setP2] = useState('')
    const [p3, setP3] = useState('')
    const [p4, setP4] = useState('')

    const [v1, setV1] = useState('')
    const [v2, setV2] = useState('')
    const [v3, setV3] = useState('')
    const [v4, setV4] = useState('')
    const [v5, setV5] = useState('')

    async function Save(){
        setState((prev) => {
            if (prev) {
                let id = state.length
                return([...prev, [name, id, p1, p2, p3, p4, v1, v2, v3, v4, v5]])
            } else {
                return([[name, 0, p1, p2, p3, p4, v1, v2, v3, v4, v5]])
            }
        })
    }

    return (
        <Pressable style={styles.centeredView} onPress={() => close()}>
            <ScrollView>
                <Pressable style={styles.modalView} onPress={() => console.log('hi')}>
                    <Text style={styles.modalText}>Make new!</Text>
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        onChangeText={text => setName(text)}
                        value={name}
                        placeholder="Name"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setP1(text)}
                        value={p1}
                        placeholder="Period 1"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setP2(text)}
                        value={p2}
                        placeholder="Period 2"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setP3(text)}
                        value={p3}
                        placeholder="Period 3"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setP4(text)}
                        value={p4}
                        placeholder="Period 4"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setV1(text)}
                        value={v1}
                        placeholder="Voltage 1"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setV2(text)}
                        value={v2}
                        placeholder="Voltage 2"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setV3(text)}
                        value={v3}
                        placeholder="Voltage 3"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setV4(text)}
                        value={v4}
                        placeholder="Voltage 4"
                    />
                    <TextInput
                        textAlign='center'
                        style={styles.textInput}
                        keyboardType = 'numeric'
                        onChangeText={text => setV5(text)}
                        value={v5}
                        placeholder="Voltage 5"
                    />
                    <Pressable
                        style={[styles.button, styles.buttonClose]}
                        onPress={() => {
                            Save()
                            close()
                        }}>
                        <Text style={styles.textStyle}>Save</Text>
                    </Pressable>
                </Pressable>
            </ScrollView>
        </Pressable>
    )
}

and the state is displayed in a flat List who looks like this:

<FlatList
    data={state}
    renderItem={Item}
    keyExtractor={item => item[1]}
    ListEmptyComponent={Empty}    
    styles={styles.container}     
    />

added the change from the first answer

Upvotes: 1

Views: 1794

Answers (1)

Brandon Pelton-Cox
Brandon Pelton-Cox

Reputation: 146

Whenever you use the previous state to evaluate what the new state will be, you should wrap it in the functional state update form, which passes the previous state as the first parameter to the function. Use that parameter, NOT the current state, to evaluate whatever you need to evaluate that relies on the current state. In your example, you need to make the following change:

function Save() {
    setState((prev) => {
        if (prev) {
            return [...prev, [p1, p2, p3, p4]]
        } else {
            return [[p1, p2, p3, p4]]
        }
    }
}

Upvotes: 1

Related Questions