Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

changes to state issued from custom hook not causing re-render even though added to useEffect

I have a custom hook that keeps a list of toggle states and while I'm seeing the internal state aligning with my expectations, I'm wondering why a component that listens to changes on the state kept by this hook isn't re-rendering on change. The code is as follows

const useToggle = () => {
  const reducer = (state, action) => ({...state, ...action});
  const [toggled, dispatch] = useReducer(reducer, {});
  const setToggle = i => {
    let newVal;
    if (toggled[i] == null) {
      newVal = true;
    } else {
      newVal = !toggled[i];
    }
    dispatch({...toggled, [i]: newVal});
    console.log('updated toggled state ...', toggled);
  };
  return {toggled, setToggle};
};

const Boxes = () => {
  const {setToggle} = useToggle()
  return Array.from({length: 8}, el => null).map((el,i) => 
  <input type="checkbox" onClick={() => setToggle(i)}/>)
  }

function App() {
  const {toggled} = useToggle()
  const memoized = useMemo(() => toggled, [toggled])
  useEffect(() => {
    console.log('toggled state is >>>', toggled) // am not seeing this on console after changes to toggled
  }, [toggled])

  return (
    <div className="App">
      <Boxes />
    </div>
  );
}
like image 983
reactor Avatar asked Jul 21 '19 04:07

reactor


People also ask

Do custom hooks trigger a Rerender?

Turns out, it doesn't really matter, whether the state change in hooks is “internal” or not. Every state change in a hook, whether it affects its return value or not, will cause the “host” component to re-render.

Does useEffect trigger re-render?

Inside, useEffect compares the two objects, and since they have a different reference, it once again fetches the users and sets the new user object to the state. The state updates then triggers a re-render in the component.

Can I use useEffect in a custom hook?

What is the React UseEffect? The useEffect hook has superpowers that enable us to design our custom hooks. When a change occurs, it allows us to perform side effects in functional components. It allows data retrieval, DOM modification, and function execution each time a component renders.

How do you run useEffect when state changes?

You can add the state variables you want to track to the hook's dependencies array and the logic in your useEffect hook will run every time the state variables change. Copied! import {useEffect, useState} from 'react'; const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.

Why is my state hook not rerendering?

Sorry, something went wrong. exactly as @malerba118 said, state hooks will not rerender if you pass the same value/object to it. React encourages an immutable approach to ensure consistency, especially between renders. I'd go further and rewrite your handler to -

When ever the state is changed it should re render the component?

When ever the state is changed it should re render the componenet . In my case it is not re rendering component. When ever a component is re rendered this.fetchQuote inside ComponetDidMount should be called.

What happens when you remove a hook from a component?

Removing the hook or the component itself means it's not fetched in that screen anymore. State (what's cached), behaviour (which effects load and invalidate the data) and UI can be closely linked together again, so they evolve organically together with your app.

Can you specify something not to render in react?

To be honest, no. I’m not even sure that it’s true , I just seem to remember reading about it (although I might be getting confused with conditional rendering / the shouldComponentUpdate hook ). I know you can specify something not to render. As I said, I’m still learning myself, so hopefully someone who knows React well can explain it for us


1 Answers

It's because you are using useToggle twice.

  1. once in the App
  2. another one in the Boxes.

When you dispatch the action in Boxes, it's updating the toggled instance for Boxes (which is not retrieved in it).

Think of your custom hook like how you use useState. When you use useState, each component gets its own state. Same goes for the custom hook.

So there are a few ways you can address the issue.

  1. Pass the setToggle from App to Boxes via prop-drilling
  2. Use Context API (or Redux or other statement management library to pass setToggle instance in the App component down)

Here is an example of prop-drilling.
You can follow along
Edit so.answer.57130413

const Boxes = ({ setToggle }) => {
  // const { setToggle } = useToggle();
  return Array.from({ length: 8 }, el => null).map((el, i) => (
    <input key={i} type="checkbox" onClick={() => setToggle(i)} />
  ));
};

function App() {
  const { toggled, setToggle } = useToggle();

  useEffect(() => {
    console.log("toggled state is >>>", toggled); // am not seeing this on console after changes to toggled
  }, [toggled]);

  return (
    <div className="App">
      <Boxes setToggle={setToggle} />
    </div>
  );
}

Note: that I added key props in Boxes using the index i(and it is a bad practice by the way)

You can see that it's now working as you'd expect.
demo

like image 104
dance2die Avatar answered Sep 29 '22 18:09

dance2die