Janily
Janily

Reputation: 301

react hooks: useState nested other useState, setState is not working?

i write example of dynamically adding text with react useHooks.And using useState to nest. But, there is a problem, the setState is not working?

when i click the text,The setState in the component does not take effect(Normally, it is add a class to the currently clicked text and delete the class on other texts), how do i do?

Below is the specific code link:

https://codesandbox.io/s/nifty-sky-z0p9w?file=/src/App.js

Thanks!

Upvotes: 1

Views: 2589

Answers (2)

German
German

Reputation: 1166

The reason it does not work its because you're accessing the value of activeItem state inside of a function. accessing state inside of a function is not recommended cause it will always have the initial value even if the state were updated. That's why <TableCel> does not re-render since it does not know that activeItem state had already change.

I recommend that you only access the state inside of useEffect(), useCallback(), useMemo() and inside of the return statement of your component.

For Example:

function App() {
  const [state, setState] = useState('initial Value')

  function someFunction(){
    // It's not recommended to access the state inside of this function
    // cause the value of the state will always be ('initial Value')
    // even if the state were updated
    console.log(state)
  }

  useEffect(()=>{
    // It's good if you access the value of the state here
    // It will be assured it will always have the updated value
    console.log(state)
  },[state]) 

  return (
    // You can also access the value of the state inside return statement        
    <>
    {console.log(state)}
    <SomeComponent props={state}/>
    </>
  )
}

Solution:

Pass the state of activeItem by using context hook. In this way <TableCel> will really know everytime activeItem state change.

Take a look in this code in sandbox link were I use context hook to solve the problem

https://codesandbox.io/s/quiet-star-zo4ej?file=/src/App.js

Upvotes: 1

Shubham Verma
Shubham Verma

Reputation: 5054

The issue in your code is that is this:

    const [activeItem, setActiveItem] = useState(null);

You are defining this at App level. And you are expecting this will work in TableCel which in another component which has its own state.

Better way would be out your TableCel component outside from App and use state there. If you need to use inside App then pass activeItems and setActiveItem as a props as well.

Here is code:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [activeItem, setActiveItem] = useState(null);

  const TableCel = (props) => {
    const { title } = props;

    return <div>{title}</div>;
  };
  const cell = [
    {
      key: 1,
      header: <TableCel id={1} title={"Header"} />,
      render: (cvm) => (
        <>
          <p>--</p>
        </>
      )
    },
    {
      key: 2,
      header: <TableCel id={2} title={"Header"} />,
      render: (cvm) => (
        <>
          <p>--</p>
        </>
      )
    },
    {
      key: 3,
      header: <TableCel id={3} title={"Header"} />,
      render: (cvm) => (
        <>
          <p>--</p>
        </>
      )
    },
    {
      key: 4,
      header: <TableCel id={4} title={"Header"} />,
      render: (cvm) => (
        <>
          <p>--</p>
        </>
      )
    },
    {
      key: 5,
      header: <TableCel id={5} title={"Header"} />,
      render: (cvm) => (
        <>
          <p>--</p>
        </>
      )
    }
  ];
  const [cellList, addCells] = useState(cell);

  const [count, setCount] = useState(6);

  // console.log(cellList);

  const addCell = (value) => {
    const _cell = {
      key: value,
      header: <TableCel title="Header" id={value} />,
      render: (cvm) => (
        <>
          <p>--</p>
        </>
      )
    };
    addCells([...cellList, _cell]);
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <button
        onClick={() => {
          setCount(count + 1);
          addCell(count + 1);
        }}
      >
        add li
      </button>
      <div>
        {cellList
          .filter((cell) => cell.key < 60)
          .map((filteredPerson, index) => (
            <li
              key={index}
              onClick={() => {
                setActiveItem(filteredPerson.key);
              }}
              className={filteredPerson.key === activeItem ? "cel-active" : ""}
            >
              {filteredPerson.header}
            </li>
          ))}
      </div>
    </div>
  );
}

Here is the demo: https://codesandbox.io/s/morning-cookies-8eud6?file=/src/App.js:0-2086

Upvotes: 2

Related Questions