ViqMontana
ViqMontana

Reputation: 5688

React Redux - useState Hook not working as expected

I have 2 actions in redux (both async) and I'm calling them both within my functional component via dispatch; the first using useEffect and the second via a button click. What I want to do is dispatch the actions to retrieve them from an async function, then use them within my component via useState. But using the useState is not rendering.

Here is my component:

export default function Hello()
{

  const { first, second } = useSelector(state => state.myReducer);
  const dispatch = useDispatch();
  const fetchFirst = async () => dispatch(getFirst());
  const fetchSecond = async () => dispatch(getSecond());
  const fetchFixturesForDate = (date: Date) => dispatch(getFixturesForDate(date));

  const [superValue, setSuperValue] = useState('value not set');

  useEffect(() => {
    const fetch = async () => {
      fetchFirst();
      setSuperValue(first);
    };

    fetch();
  }, []);

  const getSecondOnClickHandler = async () =>
  {
    console.log('a')
    await fetchSecond();
    setSuperValue(second);
  }

  return (
    <div>
    <p>The super value should first display the value "first item" once retrieved, then display "second value" once you click the button and the value is retrieved</p>
    <p>Super Value: {superValue}</p>
    <p>First Value: {first}</p>
    <p>Second Value: {second}</p>
    <button onClick={async () => await getSecondOnClickHandler()}>Get Second</button>
    </div>
  )
}

The superValue never renders even though I am setting it, although the value from first and second is retrieved and displayed.

StackBlitz.

Any help?

Upvotes: 0

Views: 520

Answers (2)

Flagship1442
Flagship1442

Reputation: 1726

The value of first and second inside your two useEffects is set when the component mounts (I guess at that point they are undefined). So in both cases you will be setting superValue to that initial value.

You have two options:

  1. Return the first/second values back from fetchFirst and fetchSecond, so that you can retrieve them directly from the executed function, and then set superValue:
 useEffect(() => {
    const fetch = async () => {
      const newFirst = await fetchFirst();
      setSuperValue(newFirst);
    };

    fetch();
  }, []);
  1. Add separate useEffects that listen for changes to first and second
  useEffect(() => {
    setSuperValue(first)
  },[first])

  useEffect(() => {
    setSuperValue(second)
  },[second])

Upvotes: 1

Harald Gliebe
Harald Gliebe

Reputation: 7554

The value in the reducer is not necessarily set when the action is dispatched, e.g. after fetchFirst() is called. Also the await that you do in await fetchSecond(); doesn't help since the reducer function is not executed.

You could add useEffect hooks and remove the setSuperValue from the other methods, but I think the code gets quite complicated. What problem are you trying to solve in the first place?

 useEffect(() => setSuperValue(first), [first]);
 useEffect(() => setSuperValue(second), [second]);

useEffect(() => {
   const fetch = async () => {
     fetchFirst();
   };
   fetch();
 }, []);

 const getSecondOnClickHandler = async () => {
   console.log('a');
   await fetchSecond();
 };

https://stackblitz.com/edit/react-ts-hsqd3x?file=Hello.tsx

Upvotes: 1

Related Questions