question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature/Discussion: functional setState was a better version

See original GitHub issue

In React hooks, to produce an effect similar to functional setState version we will use React.useEffect. However, there is a situation which hooks don’t cover. Let’s say I have a handleChange function as following

const handleChange = (value: any, name: string, rules: Rule[]) => {
	setState({ ...state, [name]: value });
};

Now if I would want to call one function handleError(rules: Rule[], name: string, value: any) with the same arguments from parent function ( in this case handleChange), I have no way to access those arguments as useEffect hook has to be outside the function and directly inside the component.

React.useEffect(() => {
	handleError() // no access to arguments of rules, name, value
}, [state, currentField]);

Only way to access those arguments is to set them as state variable and use it inside useEffect But imagine the task would have been just passing on the arguments to yet another function i.e. setState’s callback if and only if setState supported the functional callback signature. Not sure why it was removed.

Missing to do this.

const handleChange = (value: any, name: string, rules: Rule[]) => {
    setState({ ...state, [name]: value }, handleError(rules: Rule[], name: string, value: any) );
};

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
bvaughncommented, Feb 5, 2020

The implementation of the other hook you reference (use-state-with-callback) is very naive:

const useStateWithCallback = (initialState, callback) => {
  const [state, setState] = useState(initialState);

  useEffect(() => callback(state), [state, callback]);

  return [state, setState];
};

I think an implementation of what you’re describing could look something like what I’ve written below, but there are a some potential problems which I want to enumerate first:

  1. If multiple state updates were batched into a single render/commit, it would only call the most recent callback function.
  2. Because the callback function was created before the new render, any props values it closed over might be stale. This could cause problems if your state update was batched along with something else that also updated props. (To work around this you could use another ref, but this starts to get complicated.)
const DANGEROUS_useStateWithCallback = initialValue => {
  const [state, setState] = useState({
    callback: null,
    value: initialValue
  });

  const setStateWrapper = useCallback((newValue, callback) => {
    setState({
      callback,
      value: newValue
    });
  }, []);

  const prevValueRef = useRef(null);

  useLayoutEffect(() => {
    // These are the latest state values-
    // the ones that were just rendered and are being committed.
    const { callback, value } = state;

    if (typeof callback === "function") {
      const prevValue = prevValueRef.current;
      callback(
        value, // current state value
        prevValue // previous state value
      );
    }

    // This holds the state value of the previous render.
    prevValueRef.current = value;
  }, [state]);

  return [state.value, setStateWrapper];
};

I don’t think I would recommend using such a hook as shown above. I’d suggest just using useState, useEffect, and useRef (if needed, to track previous state values) as shown in the docs.

0reactions
stale[bot]commented, May 13, 2020

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Functional setState is the future of React - freeCodeCamp
While this post is more about the “functional setState” pattern, the talk is more about understanding setState deeply.
Read more >
When to use functional setState - Stack Overflow
React will first merge the object you passed to setState() into the ... setState() calls into a single update for better performance.
Read more >
Using the State Hook - React
A Hook is a special function that lets you “hook into” React features. For example, useState is a Hook that lets you add...
Read more >
useState() vs setState() - Strings, Objects, and Arrays
The setState function is used to handle the state object in a React ... in the state similar to the string version we...
Read more >
Modifying State of a Component Directly - Sentry
For the second problem, React provides a different version of setState that takes a function instead of an object.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found