react hooks

useState

setState 可以设置具体的值,也可以通过函数,利用先前的值计算得出

setState(prevState => {
  // 也可以使用 Object.assign
  return {...prevState, ...updatedValues};
});

useEffect

若在 useEffect 中使用 useState, 那么 useEffect 的第二个条件参数(依赖项)不能设置对象,否则进入死循环。因为若设置对象,则每次都会进入到 useEffect 中,由于在 useEffect 中调用 setState,则会导致页面重新渲染,而页面重新渲染,就又会进入到 useEffect中......

useReducer

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

它是 useState 的替换方案。

const initialState = {count: 0};

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

useDispatch & useSelector: 可以完全代替 redux

import { useDispatch, useSelector } from 'dva';

const dispatch = useDispatch();

// 从model仓库中取数据
const { projectList } = useSelector((state) => state.appInfo);
const appInfo = useSelector((state) => state.appInfo);

useCallback / useMemo

用于缓存数据,优化性能的钩子函数,

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

共同作用:

当依赖数据发生变化时,才会调用传进去的回调函数去重新计算结果,起到一个缓存的作用

不同作用:

  • useMemo 缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态
  • useCallback 缓存的结果是函数,主要用于缓存函数。但它需要和React.memo 配套使用,缺少任意一个都可能导致性能不升反降

useCallback

用于某函数无需反复渲染。

map.addEventListener('rightclick', onRightClick);

const onRightClick = useCallback(() => {
        const markerMenu = new BMap.ContextMenu();
        markerMenu.addItem(
            new BMap.MenuItem('设为终点', (e) => {
                const geoc = new BMap.Geocoder();
                let address = '';
            }),
        );
        map.addContextMenu(markerMenu);
    }, [map]);
    

useMemo

相当于class组件的 pureComponent , 可以缓存整个函数组件

useRef

const refContainer = useRef(initialValue);

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

三种用法:

(一):命令式的访问子组件

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

(二):非常方便的保存任何值

const draggingRef = useRef(null);

const handleDragStart = e => {
    e.stopPropagation?.();
    draggingRef.current = e.currentTarget;
};
const formRef = useRef(null);

    useEffect(() => {
        formRef.current = form;
    }, [form]);

    useEffect(() => {
        dispatch({
            type: `${currentModel}/overrideStateProps`,
            payload: {
                accessoryInfoForm: formRef,
            },
        });
    }, [dispatch, formRef, currentModel]);

(三):访问DOM的主要方式

将 ref 对象以 <div ref={myRef} /> 形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点。

当 ref 对象内容发生变化时,useRef不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。

useImperativeHandle

useImperativeHandle 可以让我们在使用 ref 时自定义暴露给父组件的实例值,即有选择性的暴露出去,而不是全部暴露。

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

useLayoutEffect

useEffect 用法一样。但 useEffect 有延迟,而 useLayoutEffect 没有延迟,它在读取DOM布局后就同步触发重渲染。即:在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

一开始先用 useEffect,只有当它出问题的时候再尝试使用 useLayoutEffect

若要从服务端渲染的 HTML 中排除依赖布局 effect 的组件,可以通过使用 showChild && <Child /> 进行条件渲染,并使用 useEffect(() => { setShowChild(true); }, []) 延迟展示组件。这样,在客户端渲染完成之前,UI 就不会像之前那样显示错乱了。

posted @ 2022-08-11 18:02  小蜗蜗蜗牛^o^  阅读(40)  评论(0编辑  收藏  举报