React Hooks

125 阅读3分钟

1. useState

使用状态,用于为函数组件引入状态(state)。

const [n, setN] = React.useState(0)

数组第一项为读接口,第二项为写接口,'0'为初始值。

注意事项

1.不能局部更新

如果 state 是一个对象,我们无法部分 setState,因为 setState 不会自动合并属性。我们可以使用...将之前的属性拷贝一遍,再将需要更新的属性覆盖掉。

2.地址需要改变

如果 setState(obj) 的 obj 地址没有改变,那么 React 就会认为数据没有变化。

3.接受函数、

  • useState
const [state, setState] = useState(()=>{
  return initialState
})

该函数返回初始state,且只执行一次。好处是减少多余的计算过程。

  • setState
setN(i => i +1)

接受一个数字,并将它加一。

2. useReducer

useState 的复杂版。

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

它接受一个形式如同(state, action) => newState这样的 reducer 函数,并返回一个数组。第一个元素为当前的 state ,第二个元素为其配套的 dispatch 方法。

下面是一个简单加乘操作的例子:

const initial = {
  n: 0
};

const reducer = (state, action) => {
  if (action.type === "add") {
    return { n: state.n + action.number };
  } else if (action.type === "multi") {
    return { n: state.n * 2 };
  } else {
    throw new Error("unknown type");
  }
};

function App() {
  const [state, dispatch] = useReducer(reducer, initial);
  const { n } = state;
  const onClick = () => {
    dispatch({ type: "add", number: 1 });
  };
  const onClick2 = () => {
    dispatch({ type: "multi", number: 2 });
  };
  return (
    <div className="App">
      <h1>n: {n}</h1>
      <button onClick={onClick}>+1</button>
      <button onClick={onClick2}>x2</button>
    </div>
  );
}

点击查看运行结果

3. useContext

组件之间的共享。

使用方法

  1. 使用C = createContext(initial)创建上下文
  2. 使用<C.provider>圈定作用域
  3. 在作用域内使用useContext(C)来使用上下文 上下文是局部的全局变量

代码示例

注意事项

  • uesContext 不是响应式的,而是重新渲染的过程。它在改变一个数的时候,是通过自顶向下逐级更新做到的。

4. useEffect

在函数组件中执行副作用操作。

对环境的改变即为副作用,如修改document.title

useEffect 每次 render 后运行。

用途

  • 作为 componentDidMount 使用, [] 作第二个参数。
  • 作为 componentDidUpdate 使用,可指定依赖。
  • 作为 componentWillUnmount 使用,通过 return 。 以上三种用途可同时存在;如果同时存在多个 useEffect ,会按照出现次序执行。

代码示例

5. useLayoutEffect

布局副作用。

它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

特点

useLayoutEffect 总是比 useEffect 先执行。

为了用户体验,优先使用 useEffect(优先渲染)。

6. useMemo useCallback

const memoizedValue = useMemo(() => value, [m, n]);

第一个参数是()=>value,第二个参数是依赖[m, n],只有当依赖变化时,才会计算出新的 value ,如果依赖不变,那么就重用之前的 value 。这种优化有助于避免在每次渲染时都进行高开销的计算。

如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。

注意

如果你的 value 是个函数,那么你就要写成useMemo(() => (x) => console.log(x)) ,这是一个返回函数的函数,我们可以使用useCallback来简化它。

useCallback

用法

useCallback(x => log(x), [m])等价于useMemo(() => x => log(x), [m])

7. useRef

useRef 返回一个可变的ref对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。

const count = useRef(0) // 初始化
useEffect(() => {
  console.log(count.current); // 读取
 };

forwardRef

用于将 ref 转发给子组件。

示例代码

useImperativeHandle

使用 ref 时自定义暴露给父组件的实例值。

8. 自定义 Hook

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。

const useList = () => {
  const [list, setList] = useState(null);
  useEffect(() => {
    ajax("/list").then(list => {
      setList(list);
    });
  }, []); // [] 确保只在第一次运行
  return {
    list: list,
    addItem: name => {
      setList([...list, { id: Math.random(), name: name }]);
    },
    deleteIndex: index => {
      setList(list.slice(0, index).concat(list.slice(index + 1)));
    }
  };
};
export default useList;

这段代码中,useList()就是一个自定义 Hook 。

使用自定义 Hook

function App() {
  const { list, deleteIndex } = useList();
  return (
    <div className="App">
      <h1>List</h1>
      {list ? (
        <ol>
          {list.map((item, index) => (
            <li key={item.id}>
              {item.name}
              <button onClick={() => { deleteIndex(index); }}>x</button>
            </li>
          ))}
        </ol>
      ) : ( "加载中..." )}
    </div>
  );
}

示例代码