How can I force component to re-render with hooks in React?

前端 未结 16 2209
误落风尘
误落风尘 2020-11-29 00:28

Considering below hooks example

   import { useState } from \'react\';

   function Example() {
       const [count, setCount] = useState(0);

       return         


        
相关标签:
16条回答
  • 2020-11-29 01:09

    Alternative to @MinhKha's answer:

    It can be much cleaner with useReducer:

    const [, forceUpdate] = useReducer(x => x + 1, 0);
    

    Usage: forceUpdate() - cleaner without params

    0 讨论(0)
  • 2020-11-29 01:10

    Solution in one single line:

    const [,forceRender] = useReducer((s) => s+1, 0)
    

    You can learn about useReducer here. https://reactjs.org/docs/hooks-reference.html#usereducer

    0 讨论(0)
  • 2020-11-29 01:11

    One line solution:

    const useForceUpdate = () => useState()[1];

    useState returns a pair of values: the current state and a function that updates it - state and setter, here we are using only the setter in order to force re-render.

    0 讨论(0)
  • 2020-11-29 01:13

    This is possible with useState or useReducer, since useState uses useReducer internally:

    const [, updateState] = React.useState();
    const forceUpdate = React.useCallback(() => updateState({}), []);
    

    forceUpdate isn't intended to be used under normal circumstances, only in testing or other outstanding cases. This situation may be addressed in a more conventional way.

    setCount is an example of improperly used forceUpdate, setState is asynchronous for performance reasons and shouldn't be forced to be synchronous just because state updates weren't performed correctly. If a state relies on previously set state, this should be done with updater function,

    If you need to set the state based on the previous state, read about the updater argument below.

    <...>

    Both state and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with state.

    setCount may not be an illustrative example because its purpose is unclear but this is the case for updater function:

    setCount(){
      this.setState(({count}) => ({ count: count + 1 }));
      this.setState(({count2}) => ({ count2: count + 1 }));
      this.setState(({count}) => ({ count2: count + 1 }));
    }
    

    This is translated 1:1 to hooks, with the exception that functions that are used as callbacks should better be memoized:

       const [state, setState] = useState({ count: 0, count2: 100 });
    
       const setCount = useCallback(() => {
         setState(({count}) => ({ count: count + 1 }));
         setState(({count2}) => ({ count2: count + 1 }));
         setState(({count}) => ({ count2: count + 1 }));
       }, []);
    
    0 讨论(0)
  • 2020-11-29 01:13

    Generally, you can use any state handling approach you want to trigger an update.

    With TypeScript

    codesandbox example

    useState

    const forceUpdate: () => void = React.useState()[1].bind(null, {})  // see NOTE below
    

    useReducer

    const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
    

    as custom hook

    Just wrap whatever approach you prefer like this

    function useForceUpdate(): () => void {
      return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
    }
    

    How this works?

    "To trigger an update" means to tell React engine that some value has changed and that it should rerender your component.

    [, setState] from useState() requires a parameter. We get rid of it by binding a fresh object {}.
    () => ({}) in useReducer is a dummy reducer that returns a fresh object each time an action is dispatched.
    {} (fresh object) is required so that it triggers an update by changing a reference in the state.

    PS: useState just wraps useReducer internally. source

    NOTE: Using .bind with useState causes a change in function reference between renders. It is possible to wrap it inside useCallback as already explained here, but then it wouldn't be a sexy one-liner™. The Reducer version already keeps reference equality between renders. This is important if you want to pass the forceUpdate function in props.

    plain JS

    const forceUpdate = React.useState()[1].bind(null, {})  // see NOTE above
    const forceUpdate = React.useReducer(() => ({}))[1]
    
    0 讨论(0)
  • 2020-11-29 01:17

    You can simply define the useState like that:

    const [, forceUpdate] = React.useState(0);
    

    And usage: forceUpdate(n => !n)

    Hope this help !

    0 讨论(0)
提交回复
热议问题