Kian Soltani
Kian Soltani

Reputation: 97

Updating a state array

I've a main container which provide a context to its childrens :

import React, {useRef, useEffect, useState, ReactNode, createContext, useContext} from 'react';
import Provider from "./Provider";
import Consumer from "./Consumer";

export const context = createContext<AppContextType | undefined>(undefined);

type AppContextType = {
    actions: { addOne?: (t: string) => void };
}

const TestContainer = () => {
    const [actions, setActions] = useState<{ addOne?: (t: string) => void }>({})

    return (
        <context.Provider value={{
            actions: actions
        }}>
            <Provider setActions={setActions}/>
            <Consumer />
        </context.Provider>
    )
}

export default TestContainer

I display a simple list from a state in this component. I also provide a function to the context (AddOne) so that other childrens can modify the list.

import React, {useEffect, useState} from 'react';

type ProviderProps = {
    setActions: React.Dispatch<React.SetStateAction<{
        addOne?: ((t: string) => void)
    }>>
}

const Provider = ({setActions}: ProviderProps) => {
    const [lines, setLines] = useState<string[]>([])

    function AddOne(text: string) {
        const linesTmp = lines;
        linesTmp.push(text)
        setLines(linesTmp)
    }

    useEffect(() => {
        setActions({addOne: AddOne})
    }, [])

    return (<>
        <h1>Lines : </h1>
            {lines.map((line, index) => (
            <p key={index}>{line}</p>
        ))}
    </>)
}

export default Provider;

There, I call the addOne function through the context.

import React, {useContext, useEffect, useState} from 'react';
import {context} from "./TestContainer";

const Consumer = () => {
    const appContext = useContext(context);

    function onButtonClick(){
        if (appContext?.actions.addOne) appContext.actions.addOne("test")
    }

    return (<div>
        <button onClick={onButtonClick}>AddTest</button>
    </div>)
}

export default Consumer;

The function is called as expected and the setLines(linesTmp) is triggered, however, the list does not update. Strangely, using React Developer Tools, I can see that the state have been updated.

Here is a codeSandbox : https://codesandbox.io/s/serene-chatelet-qlhic?file=/components/Provider.tsx https://qlhic.sse.codesandbox.io/

Edit : Creating a new array as suggested fix part of the issue : setLines([...lines, text]). However, state still reset between each call. I updated the Codesandbox

Upvotes: 0

Views: 63

Answers (2)

Mohsen Parvar
Mohsen Parvar

Reputation: 176

you should create new array before setting the state

const linesTmp = [...lines];
linesTmp.push(text)

should solve your problem

also please use a key for your list

Upvotes: 1

Viet
Viet

Reputation: 12787

You can not mutate state directly. Need to pass a new array in setLines

function AddOne(text: string) {
    setLines([...lines, text])
}

Upvotes: 1

Related Questions