Home > Blockchain >  React state is always undefined when invoke onClick event
React state is always undefined when invoke onClick event

Time:02-14

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!

Screenshot from the console shows that no matter how many times I invoke the function, the value always is undefined!

Nevertheless, 'React Developer Tools' shows that indeed these states have values!!

enter image description here

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.

  • Related