React hook介绍以及使用

hook官方文档

React hook16.8新增特性,不能在class组件中使用

只能在函数组件的最外层调用hook,不要在循环、条件判断或子函数中调用

useState

通过在函数组件里调用它来给组件添加一些内部 state。React 会在重复渲染时保留这个 state。useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并。

看下面两段代码:

  const [count, setCount] = useState(1);
  const handleClick = () => {
    for (let i = 0; i < 3; i++) {
      setCount(count + 1);
    }
  };

点击button, count值只增加了1,这里可以看出这样修改状态是异步的,执行并没有立即生效,每次拿到的值都是初始值

  const [count, setCount] = useState(1);
  const handleClick = () => {
    for (let i = 0; i < 3; i++) {
      setCount((pre) => {
        return pre + 1;
      });
    }
  };

点击button,但这时count值增加了3,这里方法中的参数,是把上一次调用setCount方法得到的值回传回来,这样就能保证每次都+1了。

另外,useState的初始值需要经过比较复杂的方法计算得到建议写成如下方式:

  const [count, setCount] = useState(() => {
    return computedCount();//这个计算很复杂
  });

如果写成如下方式

const initCount = computedCount(); //这个计算很复杂

const [count, setCount] = useState(initCount);

则每次需要都要执行这个复杂计算,影响渲染

useEffect

useEffect有两个参数,useEffect(function, array?) : 第一个参数是一个方法,第二个参数可选,为数组类型。当第二个参数不填时,每次渲染都会执行第一个参数的方法;当第二个参数设置为[]空数组时,只有当组件第一次挂载时,才会执行第一个参数的方法;当第二个参数数组有值,例如[a,b],只有当a和b发生变化时,才会执行第一个参数的方法。

其中第一个参数function可以有返回值,返回值是也是一个function

例如: 

  useEffect(() => {
    //add listener
    return () => {
      //remove listener
    };
  }, []);

当第二个参数数组为空时,返回值的function 就是在这个组件卸载时被执行。这个一般用来在挂载的时候注册一些监听,卸载时,移除监听。

  useEffect(() => {
    let timer = setInterval(() => {}, time);
    return () => {
      clearInterval(timer);
    };
  }, [time]);

当第二个参数数组不为空时,例如[a, b] , 就是在a,b发生变化时,先执行返回值的function,然后再执行第一个参数的方法。这里就是开启一个定时器,当time发生变化时,先停止掉上一个定时器,然后再开启一个新的定时器。

useLayoutEffect使用方法跟useEffect基本一致,但是:

1、useEffect是在dom渲染到页面后才回去执行,useLayoutEffect是在dom渲染到页面之前执行的

2、如果有操作dom的逻辑尽量放在useLayoutEffect中,因为如果放在useEffect中的话会看到闪烁

3、useEffect能覆盖大部分场景,因为useLayoutEffect会阻塞渲染,需要小心使用

看看useLayout官方的解释:

useContext

避免一层层传递props,也可以通过redux、mbox等状态管理来解决(正常项目中写法是分开多个文件)

const TestContext = React.createContext({ name: 'zhangsan', age: 20 });

const Test = () => {
  return (
    <TestContext.Provider value={{ name: 'lisi', age: 18 }}>
      <Child />
    </TestContext.Provider>
  );
};
const Child = React.memo(() => {
  return <Grand />;
});
const Grand = React.memo(() => {
  const context = useContext(TestContext);
  return (
    <div>
      <p>{`${context.name}-${context.age}`}</p>
    </div>
  );
});
export default Test;

useReducer

也是用来实现状态管理的hook,useState就是基于useReducer实现的,useReducer可以实现比useState更复杂的状态管理逻辑

function reducer(state, action) {
  switch (action.type) {
    case 'changeName':
      return { ...state, name: action.value };
    case 'changeAge':
      return { ...state, age: action.value };
    default:
      return state;
  }
}
const initData = { name: 'zhangsan', age: 20 };
const Test = () => {
  const [person, dispatchPerson] = useReducer(reducer, initData);

  return (
    <>
      <p>{`${person.name} - ${person.age}`}</p>
      <button
        onClick={() => dispatchPerson({ type: 'changeName', value: 'lisi' })}
      >
        change name
      </button>
      <button onClick={() => dispatchPerson({ type: 'changeAge', value: 18 })}>
        change age
      </button>
    </>
  );
};
export default Test;

useCallback

可以传入两个参数,第一个参数是方法,第2个参数是依赖关系,也是可选参数,返回值是第一个参数的方法,有记忆功能。只有在依赖项发生变化时,返回值才会发生变化,如果不传参数,则 每次组件渲染都会返回新的方法。

这样可以减少子组件不必要的刷新,但是第一个参数依然是会有开销,所以不用所有的方法都用useCallback包裹起来

如下代码:当count2发生变化时,addFunc没有变化,child这个子组件就不会重新绘制了。

const Test = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(1);
  const addFunc = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  const doubleFunc = () => {
    setCount2(count2 * 2);
  };
  return (
    <>
      <p>{`${count}-${count2}`}</p>
      <Child add={addFunc} />
      <button onClick={doubleFunc}>double</button>
    </>
  );
};
const Child = React.memo((props: { add: any }) => {
  console.log('child load');
  return <button onClick={props.add}>add</button>;
});
export default Test;

useMemo

可以传入两个参数,第一个参数是方法,用来进行一些计算,并将结果返回,第2个参数是依赖关系,也是可选参数,返回值是第一个参数的方法return出去的值,只有在依赖项发生变化时,才会重新执行第一个参数的方法,如果不传参数,则 每次组件渲染都会重新计算。

useMemo是针对于当前组件高开销的计算的优化 ,具有记忆功能,假如页面上有一个比较大的高开销的计算,每次set一个值的时候,都会重新执行计算函数,这样的话,很占内存,这时候可以用到useMemo

// 假设computedNum()是一个计算量非常大的方法

const computeValue = useMemo(() => computedNum(num), [num]);

useCallback 对于子组件渲染优化

useMemo 对于当前组件高开销的计算优化

useRef

1、可以帮助我们获取dom和react组件实例

2、也可以用来存储变量,但是页面不会重新render

forwardRef可以在父组件中操作子组件的ref对象,并且将ref对象作为一个参数传递给子组件

具体用法如下:

const Test = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  const childRef = useRef<HTMLInputElement>(null);

  const handleFocus = () => {
    inputRef?.current?.focus();
  };
  const handleChildFocus = () => {
    childRef?.current?.focus();
  };
  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleFocus}>Focus current input</button>
      <ChildForward ref={childRef} />
      <button onClick={handleChildFocus}>Focus child input</button>
    </>
  );
};
function Child(props, ref) {
  return <input ref={ref} />;
}
const ChildForward = forwardRef(Child);

export default Test;

useImperativeHandle

父组件可以调用子组件中的方法

const Test = () => {
  const childRef: any = useRef();

  const handleAdd = () => {
    childRef?.current?.addFunc();
  };
  return (
    <>
      <button onClick={handleAdd}>add</button>
      <Child onRef={childRef} />
    </>
  );
};

const Child = React.memo((props: { onRef: any }) => {
  const [count, setCount] = useState(0);
  const add = () => {
    setCount(count + 1);
  };
  useImperativeHandle(
    props.onRef,
    () => {
      return {
        addFunc: add,
      };
    },
    [count]
  );
  return <p>{count}</p>;
});

自定义hook,可以看看官方文档,讲的很清楚。

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

......
const isOnline = useFriendStatus(props.friend.id);
......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React中,使用上下文(context)可以方便地在组件之间共享数据。通过使用ReactHooks中的useContext钩子,我们可以更简单地使用上下文。 首先,我们需要创建一个上下文对象,可以使用React的createContext函数来创建。这个上下文对象可以包含我们想要共享的任何类型的数据。 然后,在函数组件中使用useContext钩子来读取上下文的值。使用useContext时,我们需要将上下文对象作为参数传递给它,它将返回上下文的当前值。 需要注意的是,尽管可以使用useContext读取上下文的值,但我们仍然需要在上层组件树中使用<MyContext.Provider>来为下层组件提供上下文。这个<MyContext.Provider>组件接收一个value属性,用于传递上下文的值给子组件。 下面是一个使用原始写法和使用context hook写法的例子: 原始写法: ``` import React from 'react'; // 创建一个上下文,初始化值 const MyContext = React.createContext({name: 'twinkle', age: 18}); // 文本组件 function TextComp() { return ( <> <MyContext.Consumer> {value => ( <> <p>名字:{value.name}</p> <p>年龄:{value.age}</p> </> )} </MyContext.Consumer> </> ); } // 测试组件 export default function TestContextComp() { return ( <div> <MyContext.Provider value={{name: 'cll', age: 18}}> <TextComp/> </MyContext.Provider> </div> ); } ``` 使用context hook写法: ``` import React, { useContext } from 'react'; // 上下文数据的结构 interface ICtxP { name: string; age: number; } // 创建一个上下文 const MyContext = React.createContext<ICtxP>({ name: 'twinkle', age: 18 }); // 文本组件 function TextComp() { const value = useContext(MyContext); // 使用上下文hook return ( <> <p>名字: {value.name}</p> <p>年龄: {value.age}</p> </> ); } export default function TextContextHook() { return ( <div> <MyContext.Provider value={{ name: 'cll', age: 18 }}> <TextComp/> </MyContext.Provider> </div> ); } ``` 无论是原始写法还是使用context hook写法,它们都可以实现在React使用上下文来共享数据。使用context hook写法可以减少代码量并简化组件的结构,使其更加简洁和易于理解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [react 进阶hook 之 context Hook](https://blog.csdn.net/qq_41499782/article/details/115493691)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值