react,umi内置钩子概念与应用

267 阅读2分钟
  1. useState【维护状态】
  2. useEffect【副作用操作】
  3. useContext【使用共享状态】
  4. useReducer【类似redux】
  5. useCallback【缓存函数】
  6. useMemo【缓存值】
  7. useRef【访问DOM】
  8. useImperativeHandle【使用子组件暴露的值/方法】
  9. useLayoutEffect【完成副作用操作,阻塞浏览器绘制】
  10. umi - useRequest

useState

普通更新 / 函数式更新 state

useEffect

  1. 我依赖了某些值,但是我不要在初始化就执行回调方法,我要让依赖改变再去执行回调方法
  2. getData异步请求
  3. useCallback缓存,提高性能

useContext

组件共享此类值得方式,不必显式通过组件数传递props

useReducer

类似redux

useCallback

缓存函数,

react中只要父组件的 render 了,那么默认情况下就会触发子组

的 render,react提供了来避免这种重渲染的性能开销的一些方法:

避免必要得渲染,使用React.memo

Reace.memo只会对props做浅比较,也就是父组件重新render之后会传入

不同引用的方法 getList,浅比较之后不相等,导致子组件还是依然会渲染。

可以用useCallback解决

useMemo

不管render多少次,案例时间戳都不会改变,已经被缓存。缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态。

useRef

  1. 访问DOM,操作DOM,点击按钮聚焦

  2. 还有保持在ref对象在组件得整个生命周期内保持不变

  3. 操作组件得DOM,用到React.forwardRef高阶组件

useImperativeHandle

父组件调用到子组件暴露的属性/方法

  • vue类似的操作 this.$ref.xxx操作dom或者调用子组件的值/方法

useLayoutEffect

DOM变更之后同步调用 effect。同步刷新,阻塞浏览器绘制。

案例:

  • ant 有个Menu,在窗口缩小放大后tab会自动调整,用useEffect的话不阻塞绘制就会看起来效果不好,这里用了useLayoutEffect,ant.design/components/… 。不过这里resize的时候还有用到requestAnimationFrame,合适的频率去刷新动画,节省CPU,函数节流。

参考案例:

codesandbox.io/s/useeffect…

import React, {
  componentDidMount,
  useState,
  useEffect,
  useRef,
  useCallback,
  useContext,
  useMemo,
  forwardRef,
  useImperativeHandle
} from "react";
import "./style.css";

// useCallback React.PureComponent、React.memo ,shouldComponentUpdate() 的应用
const Index = () => {
  const [count, setCount] = useState(0);
  const getList = useCallback(n => {
    return Array.apply(Array, Array(n)).map((item, i) => ({
      id: i,
      name: "张三" + i
    }));
  }, []);
  return (
    <>
      <Child getList={getList} />
      <button onClick={() => setCount(count + 1)}>count+1</button>
    </>
  );
};
const Child = React.memo(({ getList }) => {
  console.log("child-render", getList(10));
  return (
    <>
      {getList(10).map(item => (
        <div key={item.id}>
          id: {item.id}, name: {item.name}
        </div>
      ))}
    </>
  );
});
// useRef
const Child1 = forwardRef((props, ref) => {
  return <input ref={ref} />;
});
const Index1 = () => {
  const inputEl = useRef(null);
  const handleFocus = () => {
    inputEl.current.focus();
  };
  // <Child1 ref={inputEl} />
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={handleFocus}>Focus</button>
    </>
  );
};
// useImperativeHandle
const Index2 = () => {
  const inputEl2 = useRef();
  useEffect(() => {
    console.log(inputEl2.current.someValue);
  }, []);
  return (
    <>
      <Child2 ref={inputEl2} />
      <button
        onClick={() => {
          inputEl2.current.setValue2(val => val + 1);
        }}
      >
        累加子组件的Value
      </button>
    </>
  );
};
const Child2 = forwardRef((props, ref) => {
  const inputEl2 = useRef();
  const [value2, setValue2] = useState(0);
  useImperativeHandle(ref, () => ({
    setValue2,
    someValue2: "test"
  }));
  return (
    <>
      <div>child-value:{value2}</div>
      <input ref={inputEl2} />
    </>
  );
});

export default function App() {
  // useState
  const [count, setCount] = useState(0);
  const [obj, setObj] = useState({ id: 1 });
  componentDidMount = () => {};
  // useEffect
  useEffect(() => {
    console.log("init");
  }, []);
  // 初始化不执行,依赖改变了再去执行回调
  const firstLoad = useRef(true);
  useEffect(() => {
    if (firstLoad.current) {
      firstLoad.current = false;
      return;
    }
    console.log("do something");
  }, [obj]);
  // 异步方法, 请求初始化后,点击按钮再度调用
  // const getData = async () => {
  //   console.log("do something2");
  //   setCount(10);
  // };
  const [detail, setDetail] = useState({ id: 1 });
  async function xxx({ id }) {
    return {
      id,
      message: "ok"
    };
  }
  const getData = useCallback(async () => {
    console.log("do something2");
    const d = await xxx({ id: 2 });
    setDetail(d);
    console.log("d", d);
  }, []);
  useEffect(() => {
    console.log("useEffect getData", detail);
    getData();
  }, [getData]);
  const handleClick = () => {
    getData();
  };
  // useContext 共享主机值,不用逐层传递props
  const obj1 = {
    value: 1
  };
  const obj2 = {
    value: 2
  };
  const Obj1Context = React.createContext(obj1);
  const Obj2Context = React.createContext(obj2);
  // 子级
  const ChildComp = () => {
    return <ChildChildComp />;
  };
  // 孙级或更多级
  const ChildChildComp = () => {
    const obj1 = useContext(Obj1Context);
    const obj2 = useContext(Obj2Context);
    return (
      <>
        <div>{obj1.value}</div>
        <div>{obj2.value}</div>
      </>
    );
  };
  // useReducer
  const initialState = [{ id: 1, name: "张三" }, { id: 2, name: "李四" }];
  const reducer = (state, { type, payload }) => {
    switch (type) {
      case "add":
        return [...state, payload];
        defalt: throw new Error();
    }
  };
  const List = () => {
    const [state, dispatch] = userReducer(reducer, initialState);
    return (
      <>
        List: {JSON.stringify(state)}
        <Button
          onClick={() =>
            dispatch({
              type: "add",
              payload: {
                id: 3,
                name: "王五"
              }
            })
          }
        />
      </>
    );
  };
  // useMemo
  const getNumUseMemo = useMemo(() => {
    return `${+new Date()}`;
  }, []);
  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <p>Start editing to see some magic happen :)</p>
      <div>count: {count}</div>
      <div>obj: {JSON.stringify(obj)}</div>
      <button
        onClick={() => {
          setObj(prevObj => ({
            ...prevObj,
            ...{ id: 2, name: "test" }
          }));
        }}
      >
        merge
      </button>
      <hr />
      <button onClick={handleClick}>getData</button>
      <hr />
      <Obj1Context.Provider value={obj1}>
        <Obj2Context.Provider value={obj2}>
          <ChildComp />
        </Obj2Context.Provider>
      </Obj1Context.Provider>
      <Index />
      <hr />
      <Index1 />
      {getNumUseMemo}
      <hr />
      <Index2 />
    </div>
  );
}

umi- useRequest

zhuanlan.zhihu.com/p/106796295

和apolle的useQuery钩子很像,其实也是加强了业务方面的能力

  • 防抖+节流设置
  • 多并发请求 loading延迟
  • 分页?重置分页,重新发起请求
  • 多tab同步操作

参考

segmentfault.com/a/119000003…