HOOK的理解

 

—在React 16.8之前,我们会称呼一个叫无状态组件。其实也是一个函数。但是它不具备react一些比如State,生命周期等特性。它作为单纯的展示一个ui组件是最优的。但是React 16.8的到来。无状态组件变成了。有状态组件,可以叫函数式组件。以下的Hook的出现,改变了函数式组件的命运。使它同样具备react的一些特性。你可以自己感受Hook的书写感觉。

1.useState

@1.useState()返回的是一个数组,可以使用es6的解构出来,第一值,表示当前变量的值,第二个值,表示改变当前值得方法。

注意的是:如果变量值是基本类型的,可以常规操作。如果是引用类型的时候。我们在改变引用类型值之前,先深拷贝当前值,再来操作深拷贝的值。改变后,通过useState()解构出来的第二值来操作。就好了。如果你不深拷贝,你会发现页面不会渲染。

2.useEffect

@1.useEffect()有两个参数,第一个是回调函数,可以放一些你要执行的方法。第二个参数是可选的,如果不知道监听某个值,可以直接传递一个空数组。表示useEffect只执行一次。如果你要监听某个值在变化的时候,需要触发页面渲染。可以在空数组中添加你要监听的值。

@2.在函数组件中,如果页面刚进入就要看到数据,怎么办。我们可以把请求方法放到useEffect。

@3.useEffect的返回值,可以清除一些副作用。useEffect一般是返回一个函数,它会自己执行这个函数。我们可以把清除某些副作用的代码写入返回的函数。比如清除定时器。

3.自定义Hook

@1.在我们自己写一个Hook的时候,注意必须要以use为开头。比如你的自定义Hook叫useXXXX.自定义Hook可以提炼一些公共逻辑。封装在一起。使用自定义Hook的使用,就像调用一个function一样的。

@2.自定义hook实现一个componentDidMount

import { useEffect } from 'react';

const useMount = (fn: () => void) => {
  useEffect(() => {
    fn();
  }, []);
};

export default useMount;

4.memo (函数式组件性能优化的一个手段)

1.认知:

@1: memo是一个高阶组件,类似于React.PureComponent,不同于React.memo是function组件,React.PureComponent是class组件。

@2: 这种方式依然是一种对象的浅比较,有复杂对象时无法render。在memo中可以自定义其比较方法的实现。
memo接收两个参数,一个是组件,一个是函数。这个函数就是定义了memo需不需要render的钩子。
比较前一次的props跟当前props,返回true表示不需要render。

2.代码演示

父组件

import React, { Component, useCallback, useState } from 'react';
import { Button } from 'antd';
import Son from './Components/Son';

export default () => {
  const [value, setValue] = useState(false);

  return (
    <div>
      <Button onClick={() => setValue(!value)}>父组件点击</Button>
      <div>{`${value}`}</div>
      <Son />
    </div>
  );
};

子组件

import React, { memo } from 'react';
import { Button } from 'antd';

const App = ({ setValue }: Record<string, any>) => {
  console.log('我是子组件');
  return (
    <>
      <Button>子组件</Button>
    </>
  );
};

export default App;

@1:没有使用memo的效果

@2:使用了memo的效果

3.memo的第二个参数的使用。

父组件

import React, { Component, useCallback, useState } from 'react';
import { Button } from 'antd';
import Son from './Components/Son';

export default () => {
  const [value, setValue] = useState(false);

  return (
    <div>
      <Button onClick={() => setValue(!value)}>父组件点击</Button>
      <div>{`${value}`}</div>
      <Son value={{ name: value }} />
    </div>
  );
};

子组件

import React, { memo } from 'react';
import { Button } from 'antd';
import isEqual from 'lodash/isEqual';

const App = ({ setValue }: Record<string, any>) => {
  console.log('我是子组件');
  return (
    <>
      <Button>子组件</Button>
    </>
  );
};

export default memo(App, (preProps, nextProps) => {
  if (isEqual(preProps, nextProps)) {
    return true; // 返回true表示不用渲染
  }
  return false;
});

效果图

 5.useMemo (返回缓存的值)

1.认知

把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算

记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo

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

2.代码演示

@1:没有使用useMemo的情况

import React, { useMemo, useState } from 'react';
import { Button } from 'antd';

export default () => {
  const [value, setValue] = useState(false);
  const [data, setData] = useState<Record<string, any>[]>();

  const defaultValue = () => {
    console.log('value', value);
    return value;
  };

  return (
    <div>
      <Button onClick={() => setValue(!value)}>改变value</Button>

      <Button onClick={() => setData([{ id: 1 }])}>改变其他</Button>

      <div>{`${defaultValue()}`}</div>
    </div>
  );
};

效果如下

@2:使用useMemo的情况

import React, { useMemo, useState } from 'react';
import { Button } from 'antd';

export default () => {
  const [value, setValue] = useState(false);
  const [data, setData] = useState<Record<string, any>[]>();

  const defaultValue = useMemo(() => {
    console.log('使用useMemo', value);

    return value;
  }, [value]);

  return (
    <div>
      <Button onClick={() => setValue(!value)}>改变value</Button>

      <Button onClick={() => setData([{ id: 1 }])}>改变其他</Button>

      <div>{`${defaultValue}`}</div>
    </div>
  );
};

效果如下

6.useCallback (返回缓存函数)

1.认知

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

2.举个🌰,避免子组件没必要的渲染,我们会使用到memo和useCallback配合使用。

子组件

import React, { memo } from 'react';
import { Button } from 'antd';

const App = ({ getData }: Record<string, any>) => {
  console.log('我是子组件');
  return (
    <>
      <Button onClick={() => getData()}>子组件</Button>
    </>
  );
};

export default memo(App);

父组件

import React, { useCallback, useState } from 'react';
import { Button } from 'antd';
import Child from './Components/Son';

export default () => {
  const [value, setValue] = useState(false);

  const getData = useCallback(() => {
    console.log(1);
  }, []);

  return (
    <div>
      <Button onClick={() => setValue(!value)}>改变value</Button>
      <Child getData={getData} />
    </div>
  );
};

效果如下

总结

1.子组件里面用的memo是浅比较,而子组件的props是一个函数getData,每次父组件render时,都会重新生成一个函数,所以浅比较毫无意义。useCallback缓存函数和memo的结合解决了这问题。

2.解决上述问题还可以通过memo的第二个参数,来手动阻止不必要的渲染。(不是了解memo,上文有仔细介绍自行查阅)

 

7.useContext

1.认知

接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。

当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。即使祖先使用 React.memo 或 shouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。

2.代码实现

父组件

import React, { useState } from 'react';
import Child from './Components/Son';
import MyContext from './Components/context';

const data = [{ id: 1, value: '来自父组件的值' }];

export default () => {
  const [newData, setNewData] = useState(data);

  return (
    <MyContext.Provider value={newData}>
      <button onClick={() => setNewData([])}>父组件清空数据</button>
      <Child />
    </MyContext.Provider>
  );
};

子组件

import React, { useContext } from 'react';
import MyContext from '../context';

const App = () => {
  const data = useContext(MyContext);

  console.log('我是子组件');
  return (
    <>
      <div>子组件:{JSON.stringify(data)}</div>
    </>
  );
};

export default App;

context 对象

import React from 'react';

const MyContext = React.createContext({});

export default MyContext;

效果如下

总结

通常我们父子通信使用的是props,useContext适用于套娃的情况,父套子,子套孙子,孙子套曾孙子,是一个比较理想的传值方式。(上图为了方便演示,只套用了一层)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

见光就死123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值