[React] useReducer with lazy initializtion

const [state, dispatch] = useReducer(reducer, initialArg, init);

The third arguement 'init' is a lazy initialization which is a function.

It is useful when you want to reset the state or dynamiclly generate the state.

function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>

        Reset
      </button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

Or dyanmically generate the init val:

const preferDarkQuery = '(prefers-color-scheme: dark)'
function darkModeReducer(state, action) {
  switch (action.type) {
    case 'MEDIA_CHANGE': {
      return {...state, mode: action.mode}
    }
    case 'SET_MODE': {
      // make sure to spread that state just in case!
      return {...state, mode: action.mode}
    }
    default: {
      // helps us avoid typos!
      throw new Error(`Unhandled action type: ${action.type}`)
    }
  }
}
// use the init function to lazily initialize state so we don't read into
// localstorage or call matchMedia every render
function init() {
  return {
    mode:
      window.localStorage.getItem('colorMode') ||
      (window.matchMedia(preferDarkQuery).matches ? 'dark' : 'light'),
  }
}
function useDarkMode() {
  const [state, dispatch] = React.useReducer(
    darkModeReducer,
    {mode: 'light'},
    init,
  )
  const {mode} = state
  React.useEffect(() => {
    const mediaQuery = window.matchMedia(preferDarkQuery)
    const handleChange = () =>
      dispatch({
        type: 'MEDIA_CHANGE',
        mode: mediaQuery.matches ? 'dark' : 'light',
      })
    mediaQuery.addListener(handleChange)
    return () => mediaQuery.removeListener(handleChange)
  }, [])
  React.useEffect(() => {
    window.localStorage.setItem('colorMode', mode)
  }, [mode])
  // We like the API the way it is, so instead of returning the state object
  // and the dispatch function, we'll return the `mode` property and we'll
  // create a setMode helper (which we have to memoize in case someone wants
  // to use it in a dependency list):
  const setMode = React.useCallback(
    newMode => dispatch({type: 'SET_MODE', mode: newMode}),
    [],
  )
  return [mode, setMode]
}

 'init' function will override the second arguement.

More

原文地址:https://www.cnblogs.com/Answer1215/p/12456556.html