前端学习笔记-React Hooks-解释辨析

文章目录

    • 区分命名类似的React API与Hooks
      • Memo
      • Ref
      • Context
    • Redux Hooks
    • Hooks不能完全替代生命周期

区分命名类似的React API与Hooks

Memo

  • useMemo() (hook):缓存计算的结果;也可模拟useCallback;
  • React.memo(FC, checkPropsEqualFn):为函数式组件添加浅比较缓存功能,对标继承了PureComponent的类组件;

以上两者均提供缓存功能以减少不必要的重渲染,提升性能;

Ref

  • useRef() (hook):在多次渲染之间共享数据;也可以存储组件实例引用;
  • React.createRef():获得组件实例的引用;

使用 useRef 保存的数据一般是和 UI 的渲染无关的,值的变更不会触发重渲染;
当以上两者的值被作为组件ref属性时可以存储组件实例引用(useRef平常要.current才能存取,这种情况下直接就存值了)

  • 一个关于useRef 有趣的代码片段
import { useRef } from 'react';

// 创建一个自定义 Hook 用于执行一次性代码
function useSingleton(callback) {
  // 用一个 called ref 标记 callback 是否执行过
  const called = useRef(false);
  // 如果已经执行过,则直接返回
  if (called.current) return;
  // 第一次调用时直接执行
  callBack();
  // 设置标记为已执行过
  called.current = true;
}

const MyComp = () => {  
	// 使用自定义 Hook  
	useSingleton(() => {    
		console.log('这段代码只执行一次');  
	});
	return (<div>My Component</div>);
};

与useEffect(cb, [])的区别在于上述自定义hook是在函数体执行最初执行,而useEffect则是在render之后执行

Context

  • React.createContext()
    返回的对象可取出Ctx.Provider作为容器组件 取出Ctx.Consumer作为消费者组件(此时要使用 “函数作为子元素” 模式{ ctxVal => {/* 子组件可访问ctxVal */}});也可以通过设置类组件的contextType为此对象 使组件render时可访问this.context对象,但是这意味着该组件只能使用这唯一的一个context 除非包裹多层绑定了不同context的父组件……挺麻烦的;
  • useContext() (hook):
    Ctx.Provider包裹的后代函数式组件中可以把共享的value直接拿到,而不需要Ctx.Consumer + 函数子元素的模式;

二者均提供了一个变更时会触发重渲染的全局变量,但全局变量意味着:1.让调试变得困难,很难跟踪某个 Context 的变化究竟是如何产生的。2. 让组件的复用变得困难,因为一个组件如果使用了某个 Context,它就必须确保被用到的地方一定有这个 Context 的 Provider 在其父组件的路径上。

“函数作为子元素” 模式在RenderProps也有使用:

// Parent的render属性可理解为一个FC,可返回JSX,因此在HOC中可直接调用
<Parent render = { enhanceValue => <Sub val={enhanceValue}/> }/>
// HOC内:此处使用了函数子元素
return(<>{ this.props.render(this.state.enhanceValue) }</>)

Redux Hooks

把状态放在组件之外,就可以让 React 组件成为更加纯粹的UI层,很多对于业务数据和状态数据的管理,就都可以在组件之外去完成;

同时也天然提供了状态共享的能力,两个场景:

  1. 跨组件的状态共享:当某个组件发起一个请求时,将某个 Loading 的数据状态设为 True,另一个全局状态组件则显示 Loading 的状态。
  2. 同组件多个实例的状态共享:某个页面组件初次加载时,会发送请求拿回了一个数据,切换到另外一个页面后又返回。这时数据已经存在,无需重新加载。设想如果是本地的组件 state,那么组件销毁后重新创建,state 也会被重置,就还需要重新获取数据。

react-redux 的实现利用了 React 的 Context 机制去存放 Store,但是它没有Ctx.Provider的Consumer使用起来那么麻烦

import { Provider } from 'react-redux'
<Provider store={store}>
    <App />  
</Provider>

react-redux 还提供 useSelectoruseDispatch两个 Hooks,比起原来connect时所使用的两个那么长的API,hooks使用简单,命名也更好懂,实在是很舒服 :前端学习笔记-React Hooks-解释辨析_第1张图片
如果出于某种原因,如单元测试,想要获取不同的store,我们可以使用useStore()这个hook并通过新的contextAPI传递进组件树中,就像下面这样:

import React from 'react';
import { useStore } from 'react-redux';
import OtherProvider from './OtherProvider';

const Component = props => {
  const store = useStore();
  return <OtherProvider store={store}> {props.children} </OtherProvider>
}

Hooks不能完全替代生命周期

Class 组件中还有其它一些比较少用的方法,比如 getSnapshotBeforeUpdate, componentDidCatch, getDerivedStateFromError

目前 Hooks 还没法实现这些功能,如果必须用到,仍然需要用Class 组件去实现。

不过社区已经存在基于Hooks的解决方案来处理异常:

https://juejin.cn/post/6974383324148006926#heading-14

你可能感兴趣的:(阅读笔记,react)