React Hooks(六) useRuducer

一 关于useRuducer的用法

useRuducer是useState的升级版本,当状态更新逻辑比较复杂的时候,就应该考虑使用useReudcer,因为useReducer比useState更擅长描述如何更新状态,并且通过使用useReducer的dispatch能减少状态值的传递。

用法:

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

看一个简单的例子:

const initialState = {
  n: 0,
};
const reducer = (state, action) => {
  if (action.type === "add") {
    /* 规则与useState一样必须返回新的对象,不然变量值不会改变 */
    return { n: state.n + action.number };
  } else if (action.type === "multi") {
    return { n: state.n * action.number };
  } else {
    throw new Error("unknown type!");
  }
};
/* 在函数组件中使用useReducer */
const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const onclick1 = () => {
    dispatch({ type: "add", number: 1 });
  };
  const onclick2 = () => {
    dispatch({ type: "add", number: 2 });
  };
  const onclick3 = () => {
    dispatch({ type: "multi", number: 2 });
  };
  return (
    <div className="App">
      <h1>n:{state.n}</h1>
      <button onClick={onclick1}>+1</button>
      <button onClick={onclick2}>+2</button>
      <button onClick={onclick3}>x2</button>
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));

dispatch函数可以通过type的类型值对数据进行处理,尤其是在数据比较复杂的情况下可以避免多次使用setState。

看一个表单提交的例子:

const initialState = {
  name: "",
  age: 18,
  nationality: "汉族",
};
const reducer = (state, action) => {
  switch (action.type) {
    case "patch":
      return { ...state, ...action.formData };
    case "reset":
      return initialState;
    default:
      throw new Error("unknown type!");
  }
};
/* 在函数组件中使用useReducer */
const App = () => {
  const [formData, dispatch] = useReducer(reducer, initialState);
  const onSubmit = () => {
    alert("你点击了提交按钮");
  };
  const onReset = () => {
    dispatch({ type: "reset" });
  };
  return (
    <form onSubmit={onSubmit} onReset={onReset}>
      <div>
        <label>
          姓名
          <input
            type="text"
            value={formData.name}
            onChange={(e) => {
              dispatch({ type: "patch", formData: { name: e.target.value } });
            }}
          />
        </label>
      </div>
      <div>
        <label>
          年龄
          <input
            type="number"
            value={formData.age}
            onChange={(e) => {
              dispatch({ type: "patch", formData: { name: e.target.value } });
            }}
          />
        </label>
      </div>
      <div>
        <label>
          民族
          <input
            type="text"
            value={formData.nationality}
            onChange={(e) => {
              dispatch({
                type: "patch",
                formData: { nationality: e.target.value },
              });
            }}
          />
        </label>
      </div>
      <div>
        <button type="submit">提交</button>
        <button type="reset">重置</button>
      </div>
      <hr />
      {JSON.stringify(formData)}
    </form>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));

二 useContext结合useRuducer管理全局数据

看一个简单的例子:

const initState = {
  n: 0,
  m: 0,
  p: 0,
};
const reducer = (state, action) => {
  switch (action.type) {
    case "setN":
      return { ...state, n: state.n + action.number };
    case "setM":
      return { ...state, m: state.m + action.number };
    case "setP":
      return { ...state, p: state.p + action.number };
    default:
      throw new Error("unknown type!");
  }
};
/* 创建上下文对象--模拟一个Redux的作用域 */
const Context = React.createContext(null);
const App = () => {
  const [state, dispatch] = React.useReducer(reducer, initState);
  return (
    <Context.Provider value={{ state, dispatch }}>
      <N />
      <M />
      <P />
    </Context.Provider>
  );
};
const N = () => {
  const { state, dispatch } = React.useContext(Context);
  const addClick = () => {
    dispatch({ type: "setN", number: 1 });
  };
  return (
    <div>
      <h1>N组件</h1>
      <div>n:{state.n}</div>
      <div>m:{state.m}</div>
      <div>p:{state.p}</div>
      <button onClick={addClick}>+1</button>
    </div>
  );
};
const M = () => {
  const { state, dispatch } = React.useContext(Context);
  const addClick = () => {
    dispatch({ type: "setM", number: 2 });
  };
  return (
    <div>
      <h1>M组件</h1>
      <div>n:{state.n}</div>
      <div>m:{state.m}</div>
      <div>p:{state.p}</div>
      <button onClick={addClick}>+2</button>
    </div>
  );
};
const P = () => {
  const { state, dispatch } = React.useContext(Context);
  const addClick = () => {
    dispatch({ type: "setP", number: 3 });
  };
  return (
    <div>
      <h1>P组件</h1>
      <div>n:{state.n}</div>
      <div>m:{state.m}</div>
      <div>p:{state.p}</div>
      <button onClick={addClick}>+3</button>
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));

再看一个封装的比较好的例子:
storeProvider.tsx

import React, { useCallback, useEffect, useReducer } from 'react'

/**
 * @description 创建当前模块的context方法,之后在子组件中使用useContext引入
 */
export const createStoreContext = () => {
  return React.createContext<IStoreContext>({
    store: {},
    dispatch: () => { },
    action: () => { },
  })
}

/**
 * @description 创建store的组件,包裹子组件,将store以及dispatch传入当前的树中
 * @param { Object } props
 * @param { IStoreContextType } props.StoreContext
 * @param { React.ReactNode } props.children
 * @param { IReducer> } props.reducer
 * @param { any } props.initData
 * @param { IPersistConfig } [props.persistConfig]
 * @returns 包裹了store的组件
 */
export const StoreProvider: StoreProviderType = (props) => {
  const { StoreContext, children, reducer, initData = {} } = props
  // 生成store,以及dispatch
  const [store, dispatch] = useReducer(reducer, initData)
  const logDispatch = (args) => {
    const { type, payload } = args
    console.group('Module Store \n')
    console.log('action: ' + type + '\n' + 'payload: ')
    console.log(payload)
    console.groupEnd()
    dispatch(args)
  }
  useEffect(() => {
    return () => {
      console.log('Module Store ', store)
    }
  }, [store])
  // action操作方式
  const action = useCallback((callback) => {
    try {
      callback?.(store, dispatch)
      console.log('DISPATCH CALLBACK', store)
    } catch (error) {
      console.error(error.message)
    }
  }, [])
  return <StoreContext.Provider value={{ store, dispatch: logDispatch, action }}>{children}</StoreContext.Provider>
}

使用上面封装的组件:

import { StoreProvider, createStoreContext } from '../../utils/storeProvider'

export const StoreContext = createStoreContext()

export const reducer = (state, action) => {
  let _state = state
  let { type, payload } = action
  switch (type) {
    case 'init':
      return { ..._state, ...payload }
    case 'locationUpdate':
      return { ..._state, Location: { ..._state.Location, ...payload } }
    case 'bigImageChange':
      return { ..._state, BigImageTruck: { ..._state.BigImageTruck, ...payload } }
   
    default:
      return _state
  }
}

export const initData = {
  commonData: {},
  Location: {
    moduleData: {},
    stateData: null,
    styleInfoData: {
      display: true,
      componentType: 'MBLocationCity',
      subComponentType: null,
      mode: null,
      editable: null,
      sceneType: null,
    },
    defaultStateData: null,
  },
  BigImageTruck: {
    moduleData: {},
    stateData: null,
    styleInfoData: {
      display: true,
      componentType: 'MBBigImageTruck',
      subComponentType: null,
      mode: null,
      editable: null,
      sceneType: null,
    },
    defaultStateData: null,
  }
}

export default ({ children }) => {
  return (
    <StoreProvider StoreContext={StoreContext} reducer={reducer} initData={initData}>
      {children}
    </StoreProvider>
  )
}

使用:

import ModuleIndex, { StoreContext } from './module'

const PreparePage = () => {
  const { store, dispatch } = useContext(StoreContext)
  // const LocationData = useMemo(() => store.Location, [store.Location])
}

export default () => (
  <ModuleIndex>
    <PreparePage />
  </ModuleIndex>
)

你可能感兴趣的:(React,react.js)