react &react-dom

90 阅读6分钟
flushSync
 console.log('runder');
  const [num, setNum] = useState<number>(0);
  const getLo = () => {
    // 使用flushSync会使视图立即更新一次所以会render两次
    flushSync(() => {
      setNum(111);
    });
    // 因为react的更新是异步的所以只更新一次
    // setNum(111);
    setNum(2222);
  };
useState
  /* 
    setNum是修改num的方法  
    react18之后都是异步更新内部会维护一个队列
  */
  const [num, setNum] = useState<number>(0);
useEffect
  /**
   * useEffect(callback,[dependencies])
   * 后面数组中设置依赖项
   * 依赖项不设置时callback初次渲染完成和更新完成时会调用
   * 依赖项为空数组时callback初次渲染完成时会调用
   * 设置了依赖项时初次渲染完成时会调用当依赖项发生改变时会调用
   *
   *
   * callback可以返回一个函数,这个函数会在卸载组件时调用
   */
 useEffect(() => {
    const timer = setTimeout(() => {
      setNum(1);
      console.log(num);
    }, 1000);
    return () => {
      clearTimeout(timer);
    };
  }, [num]);
useLayoutEffect
使用和useEffect几乎一样
区别在于:
首先要说明的是,useLayoutEffect和useEffect很像,函数签名也是一样。唯一的不同点就是useEffect是异步执行,而useLayoutEffect是同步执行的。 当函数组件刷新(渲染)时,包含useEffect的组件整个运行过程如下:

1.  触发组件重新渲染(通过改变组件state或者组件的父组件重新渲染,导致子节点渲染)。
1.  组件函数执行。
1.  组件渲染后呈现到屏幕上。
1.  `useEffect` hook执行。

当函数组件刷新(渲染)时,包含useLayoutEffect的组件整个运行过程如下:

1.  触发组件重新渲染(通过改变组件state或者组件的父组件重新渲染,导致子组件渲染)。
1.  组件函数执行。
1.  `useLayoutEffect` hook执行, React等待useLayoutEffect的函数执行完毕。
1.  组件渲染后呈现到屏幕上。

useEffect异步执行的优点是,react渲染组件不必等待useEffect函数执行完毕,造成阻塞。
ref
/*
    React.createRef和useRef的区别
    当函数组件更新时React.createRef会重新创建ref对象浪费性能,useRef会复用之前的
    函数组件中用useref
    类组件中使用React.createRef
    当子组件是类组件时使用ref.currenth会获取到子组件实例所以可以拿到子组件的方法和state
*/

  /**
   * 方式一基于ref={函数}的方式可以把创建的dom元素赋值给变量(不推荐)
   */
  let box;
  return <div ref={(x) => (box = x)}>Login</div>;
  
    /**
   * 方式二 React.createRef 函数组件和类组件都可以用
   */
  const box = React.createRef<HTMLDivElement>();
  useEffect(() => {
    console.log(box.current);
  }, []);

  return <div ref={box}>Login</div>;
  
    /**
   * 方式三 useRef
   */
  const box = useRef<HTMLDivElement>(null);
  useEffect(() => {
    console.log(box);
  }, []);

  return <div ref={box}>Login</div>;
useRef获取子组件dom
interface ChildProps {}
const Child = forwardRef<HTMLDivElement, ChildProps>((props, ref) => {
  return <div ref={ref} />;
});
useRef获取子组件方法及事件

interface ChildProps {}
interface ChildRef {
  num: any;
  setNum: any;
}
const Child = forwardRef<ChildRef, ChildProps>((props, ref) => {
  const [num, setNum] = useState();
  useImperativeHandle(ref, () => {
    return {
      num,
      setNum,
    };
  });
  return <div>111111</div>;
});

useMemo
const Demo = function Demo() {
    let [supNum, setSupNum] = useState(10),
        [oppNum, setOppNum] = useState(5),
        [x, setX] = useState(0);

    /* 
     let xxx = useMemo(callback,[dependencies])
       + 第一次渲染组件的时候,callback会执行
       + 后期只有依赖的状态值发生改变,callback才会再执行
       + 每一次会把callback执行的返回结果赋值给xxx
       + useMemo具备“计算缓存”,在依赖的状态值没有发生改变,callback没有触发执行的时候,xxx获取的是上一次计算出来的结果
       和Vue中的计算属性非常的类似!!
       +如果不使用useMemo的话ratio的计算逻辑每次组件更新都会执行
     */
    let ratio = useMemo(() => {
        let total = supNum + oppNum,
            ratio = '--';
        if (total > 0) ratio = (supNum / total * 100).toFixed(2) + '%';
        return ratio;
    }, [supNum, oppNum]);

    return <div className="vote-box">
        <div className="main">
            <p>支持人数:{supNum}人</p>
            <p>反对人数:{oppNum}人</p>
            <p>支持比率:{ratio}</p>
            <p>x:{x}</p>
        </div>
        <div className="footer">
            <Button type="primary" onClick={() => setSupNum(supNum + 1)}>支持</Button>
            <Button type="primary" danger onClick={() => setOppNum(oppNum + 1)}>反对</Button>
            <Button onClick={() => setX(x + 1)}>干点别的事</Button>
        </div>
    </div>;
};
useCallback +memo
/**
 * 函数组件使用React.memo类组件使用React.PureComponent都会对prop内的属性进行浅比较,如果相同就不会更新该组件
 * 
 * 函数组件更新时内部定义的函数都会重新创建浪费性能
 * useCallback会缓存定义的函数(每次组件更新其定义的函数不会重新创建)
 * useCallback不要乱用一般在子组件用到父组件的方法时才用,因为useCallback本身也有逻辑也会浪费性能
 * 使用useCallback后每次函数都是同一个内存地址并且子组件使用memo后子组件就不会更新
 */
const Child = React.memo(function Child(props) {
    console.log('Child Render');
    return <div>
        我是子组件
    </div>;
});

/* 父组件 */
// 诉求:当父组件更新的时候,因为传递给子组件的属性仅仅是一个函数「特点:基本应该算是不变的」,所以不想再让子组件也跟着更新了!
//   + 第一条:传递给子组件的属性(函数),每一次需要是相同的堆内存地址(是一致的) . 基于useCallback处理!!
//   + 第二条:在子组件内部也要做一个处理,验证父组件传递的属性是否发生改变,如果没有变化,则让子组件不能更新,有变化才需要更新 . 继承React.PureComponent即可「在shouldComponentUpdate中对新老属性做了浅比较」!! 函数组件是基于 React.memo 函数,对新老传递的属性做比较,如果不一致,才会把函数组件执行,如果一致,则不让子组件更新!!
const Demo = function Demo() {
    let [x, setX] = useState(0);

    // const handle = () => { };  //第一次:0x001  第二次:0x101  第三次:0x201 ...
    const handle = useCallback(() => { }, []); //第一次:0x001  第二次:0x001  第三次:0x001 ...

    return <div className="vote-box">
        <Child handle={handle} />
        <div className="main">
            <p>{x}</p>
        </div>
        <div className="footer">
            <Button type="primary" onClick={() => setX(x + 1)}>累加</Button>
        </div>
    </div>;
};

类组件

生命周期
class ExampleComponent extends react.Component {
  // 构造函数,最先被执行,我们通常在构造函数里初始化state对象或者给自定义方法绑定this
  constructor() {}
  //getDerivedStateFromProps(nextProps, prevState)用于替换 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被调用
  // 这是个静态方法,当我们接收到新的属性想去修改我们state,可以使用getDerivedStateFromProps
  static getDerivedStateFromProps(nextProps, prevState) {
    // 新的钩子 getDerivedStateFromProps() 更加纯粹, 它做的事情是将新传进来的属性和当前的状态值进行对比, 若不一致则更新当前的状态。
    if (nextProps.riderId !== prevState.riderId) {
      return {
        riderId: nextProps.riderId
      }
    }
    // 返回 null 则表示 state 不用作更新
    return null
  }
  // shouldComponentUpdate(nextProps, nextState),有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利用此生命周期来优化react程序性能
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.id !== this.props.id
  }
  // 组件挂载后调用
  // 可以在该函数中进行请求或者订阅
  componentDidMount() {}
  // getSnapshotBeforeUpdate(prevProps, prevState):这个方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null,此生命周期必须与componentDidUpdate搭配使用
  getSnapshotBeforeUpdate() {}
  // 组件即将销毁
  // 可以在此处移除订阅,定时器等等
  componentWillUnmount() {}
  // 组件销毁后调用
  componentDidUnMount() {}
  // componentDidUpdate(prevProps, prevState, snapshot):该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,如果触发某些回调函数时需要用到 DOM 元素的状态,则将对比或计算的过程迁移至 getSnapshotBeforeUpdate,然后在 componentDidUpdate 中统一触发回调或更新状态。
  componentDidUpdate() {}
  // 渲染组件函数
  render() {}
  // 以下函数不建议使用
  UNSAFE_componentWillMount() {}
  UNSAFE_componentWillUpdate(nextProps, nextState) {}
  UNSAFE_componentWillReceiveProps(nextProps) {}
}