浅谈react hook

523 阅读4分钟

下面从三个维度去总结hook的学习

useState

为什么使用useState

在function组件中维护state的时候需要用

怎么使用useState

const [name, setName] = useState('George')

useState知识点

重点:useState的初始值只有在第一次有效

例子:当点击update name的时候,虽然Child组件是收到了,但是并没有通过useState赋值给name

const Child = React.memo(({ data }) => {
  const [name, setName] = React.useState(data);
  return (
    <Box>
      {name} --123123- {data}
    </Box>
  );
});

const Hook =()=>{
  const [name, setName] = React.useState('rose');

  return (
    <Box>
      <Button onPress={() => setName('jack')}>
        <Text>update count </Text>update name
      </Button>
      <Child data={name} />
    </Box>
  );
}

useEffect

为什么使用useEffect

在函数组件中使用class的生命周期,可以看做是componentDidMountcomponentDidUpdate 和 componentWillUnmount 这三个函数的组合。

怎么使用useEffect

useEffect(()=>{
    ...
})

useEffect知识点

姿势一:只在第一次使用使用,例如用来请求异步数据

useEffect最后,加了[]就表示只第一次执行
useEffect(()=>{
    const users = 获取用户信息()
},[])

姿势二:用来替代willUpdate等每次渲染都会执行的生命函数

useEffect最后,不加[]就表示每一次渲染都执行
useEffect(()=>{
    const users = 获取用户信息()
})

姿势三:每次渲染都执行会影响性能,所以

useEffect最后,加[],并且[]里面加的字段就表示,这个字段有更改effect才执行
useEffect(() => {
    const users = (name改变了我才获取获取用户信息())
},[name])

姿势四:如果我们之前订阅了什么,最后在willUnMount这个生命周期里面要取消订阅,可以在useEffect这样实现:

在effect的return里面可以做取消订阅的事
useEffect(() => {
    const subscription = 订阅新闻推送!
    return () => {
        取消订阅新闻推送!
    }
},[])

为什么要取消订阅?

大家都知道,render了之后会执行重新useEffect,如果useEffect里面有一个每setInterval...那么每次render了,再次执行useEffect就会再创建一个setInterval,然后就混乱了...可以把下面案例return的内容删掉感受下

  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    console.log('use effect...', count);
    const timer = setInterval(() => setCount(count + 1), 1000);
    return () => clearInterval(timer);
  });

useMemo

为什么使用useMemo

举个栗子

const Child = React.memo(({ data }) => {
  console.log('child render...', data.name);
  return (
    <Box>
      <Box>child</Box>
      <Box>{data.name}</Box>
    </Box>
  );
});

export const Hook = () => {
  console.log('Hook render...');
  const [count, setCount] = React.useState(0);
  const [name, setName] = React.useState('rose');

  const data = {
    name,
  };

  return (
    <Box>
      <Text>{count}</Text>
      <Button onPress={() => setCount(count + 1)}>update count </Button>
      <Child data={data} />
    </Box>
  );
};

当点击按钮更新的时候,effect组件就会render,一旦render之后,刷新到data,这一行代码就会生成新的内存地址对象,那么就算带着memo的child组件,也会跟着重新render。

这样多余的render,就会造成性能浪费!于是useMemo就派上用场了

怎么使用useMemo

const data = useMemo(()=>{
    return {
        name
    }
},[name])

因为使用了useMemo,它具有一个暂存的能力,暂存了上一次的name结果。在data重新刷新的时候,如果发现name值对比上一次的name并没有改变,那么这次data就不重新赋值成新的对象了。

没有新的对象,就没有新的内存地址,那么Child就不会重新render。

useMemo知识点

姿势一:首先,memo的用法是:函数组件里面的PureConponent

但是,如果函数组件被 React.memo 包裹,且其实现中拥有 useState 或 useContext 的 Hook,当 context 发生变化时,它仍会重新渲染。

姿势二:memo是浅比较,意思是,对象之比较内存地址,只要内存地址没有改变,对象里面的值怎么改变都不会触发render

useCallback

useMemo解决了值的缓存问题,那么函数就是使用useCallback

为什么使用useCallback

举个栗子

const Child = React.memo(({ data, onChange }) => {
  console.log('child render...');
  return (
    <Box>
      <Text>child</Text>
      <Box>{data}</Box>
      <TextInput onChange={onChange} />
    </Box>
  );
});

export const Hook = () => {
  console.log('Hook render...');
  const [count, setCount] = React.useState(0);
  const [name, setName] = React.useState('rose');
  const [text, setText] = React.useState('');

  const onChange = React.useCallback(e => {
    setText(e.target.value);
  }, []);

  return (
    <Box>
      <Text>{count}</Text>
      <Text>{text}</Text>
      <Button onPress={() => setCount(count + 1)}>update count </Button>
      <Child data={name} onChange={onChange} />
    </Box>
  );
};

尽管值并没有变化,但是还是重新渲染child,造成了性能浪费

怎么使用useCallback

const onChange = useCallback((e)=>{
    setText(e.target.value)
},[])

useCallback知识点

姿势一:useMemo是缓存值的,useCallback是缓存函数的

姿势二:没有依赖,添加空的依赖,就是空数组!

useContext

为什么使用useContext

等同class里面的context。

怎么使用useContext

import React from 'react';

export interface IGlobalLoadingContextValue {
  visible: boolean;
  setVisible: React.Dispatch<React.SetStateAction<boolean>>;
}

export const GlobalLoadingContext =
  React.createContext<IGlobalLoadingContextValue>({
    visible: false,
    setVisible: () => {},
  });

export const GlobalLoadingProvider: React.FC = (props: any) => {
  const [visible, setVisible] = React.useState<boolean>(false);

  return (
    <GlobalLoadingContext.Provider
      value={{
        visible,
        setVisible,
      }}>
      {props.children}
    </GlobalLoadingContext.Provider>
  );
};

自定义hook

自定义一个当resize 的时候 监听window的width和height的hook

import {useEffect, useState} from "react";

export const useWindowSize = () => {
    const [width, setWidth] = useState()
    const [height, setHeight] = useState()

    useEffect(() => {
        const {clientWidth, clientHeight} = document.documentElement
        setWidth(clientWidth)
        setHeight(clientHeight)
    }, [])

    useEffect(() => {
        const handleWindowSize = () =>{
            const {clientWidth, clientHeight} = document.documentElement
            setWidth(clientWidth)
            setHeight(clientHeight)
        };

        window.addEventListener('resize', handleWindowSize, false)

        return () => {
            window.removeEventListener('resize',handleWindowSize, false)
        }
    })

    return [width, height]
}

怎么使用

const [width, height] = useWindowSize()