Reputation: 97
I'm updating with functions that I pass from one component to its parent and then through context. I can set the state as I want but I can't access its data inside these functions. They show as the initial data. It's odd, I checked in React DevTools and the state is fine.
Here is a codeSandbox : https://codesandbox.io/s/admiring-fog-2b5gm?file=/components/Provider.tsx:859-992 Link to the page : https://2b5gm.sse.codesandbox.io/
import React, { useState, createContext } from "react";
import Provider from "./Provider";
import Consumer from "./Consumer";
export const context = createContext<AppContextType | undefined>(undefined);
type AppContextType = {
actions: {
addStrings?: (t: string[]) => void;
logStrings?: () => void;
};
};
const TestContainer = () => {
const [actions, setActions] = useState<{
addStrings?: (t: string[]) => void;
}>({});
return (
<context.Provider
value={{
actions: actions
}}
>
<Provider setActions={setActions} />
<Consumer />
</context.Provider>
);
};
export default TestContainer;
import React, { useEffect, useState } from "react";
type ProviderProps = {
setActions: React.Dispatch<
React.SetStateAction<{
addStrings?: (t: string[]) => void;
logStrings?: () => void;
}>
>;
};
const Provider = ({ setActions }: ProviderProps) => {
const [textArray, setTextArray] = useState<string[]>([]);
function addStrings(texts: string[]) {
console.log("PREVIOUS LINES: ", textArray);
console.log("NEW LINES:", [...textArray, ...texts]);
setTextArray([...textArray, ...texts]);
}
function logStrings() {
console.log("Strings : ", textArray);
}
useEffect(() => {
console.log("send functions to parent");
setActions({ addStrings: addStrings, logStrings: logStrings });
}, []);
useEffect(() => {
console.log("useEffect textArray changed to: ", textArray);
}, [textArray]);
return (
<>
<h1>Strings : </h1>
{textArray.map((line, index) => (
<p key={index}>{line}</p>
))}
</>
);
};
export default Provider;
import React, { useContext } from "react";
import { context } from "./TestContainer";
const Consumer = () => {
const appContext = useContext(context);
function onButtonAddClick() {
if (appContext?.actions.addStrings)
appContext.actions.addStrings(["test 1", "test 2", "test 3"]);
}
function onButtonLogClick() {
if (appContext?.actions.logStrings) appContext.actions.logStrings();
}
return (
<div>
<button onClick={onButtonAddClick}>Add strings</button>
<button onClick={onButtonLogClick}>Log</button>
</div>
);
};
export default Consumer;
The data in question is the textArray
state in the Provider component.
I'm calling the addStrings
and the LogStrings
methods from the Consumer
component to control it.
Expected behavior:
LogStrings
should log the actual value of the state. In this case : "test 1, test 2, test 3" but it always print the state's default value.
Upvotes: 2
Views: 887
Reputation: 50258
The reason you're seeing the initial state getting logged is because setActions
only gets called when Provider
is first mounted, when textArray
still has its initial value of empty array []
. That will be the value that logStrings
will log, even if you change textArray
externally.
To actually update logStrings
with the new textArray
value you pass in addStrings(["test 1", "test 2", "test 3"])
, you'd need to call setActions
again. This means adding textArray
to the dependencies array of the useEffect
that calls setActions
.
useEffect(() => {
console.log("send functions to parent");
setActions({ addStrings: addStrings, logStrings: logStrings });
}, [textArray]);
Upvotes: 1