So I have the following code:
export default function Component() {
const [list, setList] = useState<JSX.Element[]>();
const [active, setActive] = useState<JSX.Element>();
useEffect(()=>{
let startTab = <li key={"0000-0000-0000-0000"} onClick={() => onClick()}>Star 1</li>;
let secondTab = <li key={"0001-0000-0000-0000"} onClick={() => onClick()}>Star 2</li>;
setList(() => [startTab, secondTab]);
setActive(() => startTab);
},[])
const onClick= () =>{
console.log(list)
}
return (
<div id="BodyMain" className={css["Body-container"]}>
<div className={css["tab-header"]}>
{ (
<ul className={css["navs"]}>
{list?.map(tabItem => tabItem)}
</ul>)
}
</div>
<div className={css["tab-body"]}>{}</div>
</div>
);
}
every time I invoke onClick function it just returns undefined!
Nevertheless, 'React Developer Tools' shows that indeed these states have values!!
I am unable to change the state from onClick function because it is always undefined. What did I do wrong? and how can I fix it?
CodePudding user response:
The problem is the way that you use useEffect() hook. When you dnt have any dependencies, that hook is gonna run once. Hence setList(() => [startTab, secondTab]); just runs once
If you want to update it whenever you click, you can add a dependency for the useEffect() hook:
const [list, setList] = useState<JSX.Element[]>();
const [active, setActive] = useState<JSX.Element>();
const [ count, setCount ] = useState (0);
useEffect(()=>{
let startTab = <li key={"0000-0000-0000-0000"} onClick={() => onClick()}>Star 1</li>;
let secondTab = <li key={"0001-0000-0000-0000"} onClick={() => onClick()}>Star 2</li>;
setList(() => [startTab, secondTab]);
setActive(() => startTab);
},[count])
const onClick= () =>{
setCount(() => count 1)
console.log(list)
}
I usually use a count variable to capture any change and set it as a useEffect dependency.
Read more at https://reactjs.org/docs/hooks-effect.html
CodePudding user response:
It happens because during first render list
is undefined. And useEffect
captures onClick
function with list as undefined. Since useEffect runs only once - list inside onClick will be always undefined. But if useEffect will run,
list will be updated too inside useEffect. And as @nam-h-nguyen said , really with count
as a dependency useEffect will rerun and hence new value of list will be captured.
CodePudding user response:
Actually, there is another solution for that
const [list, setList] = useState<JSX.Element[]>();
const [active, setActive] = useState<JSX.Element>();
const [ count, setCount ] = useState (0);
let startTab = <li key={"0000-0000-0000-0000"} onClick={() =>
onClick()}>Star 1</li>;
let secondTab = <li key={"0001-0000-0000-0000"} onClick={() =>
onClick()}>Star 2</li>;
useEffect(()=>{
setList(() => [startTab, secondTab]);
setActive(() => startTab);
},[])
const onClick= () =>{
setList(() => [startTab, secondTab]);
console.log(list)
}
What I did was:
- I took startTab and seconTab out of the useEffect() hook and made them as global variables for that component so that I can use them everywhere inside that component.
- Then, I just needed to update the "list" directly inside the onClick() function.
- And I used useEffect without a dependency to update the list during the first render.
I like to do this way more than putting everything in the useEffect hook() because anything you put inside it becomes a local variable of it and you cant use it outside of the useEffect() hook.