2-2-4 & 5 & 6 React Hooks基础API

Posted 沿着路走到底

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2-2-4 & 5 & 6 React Hooks基础API相关的知识,希望对你有一定的参考价值。

- useState

- useEffect

- useRef

- useMemo

- useCallback

- useContext

useState hook

作用:管理状态,并当状态发生变化的时候反向通知React重绘。

通知方向: state hook -> React Component -> render

import useState from 'react'
function App()
    const [count, setCount] = useState(0)
    const [count, setCount] = useState<number>(0)
    const [count, setCount] = useState(() => 0)
    
    return <div>
        count
        <button onClick=() => setCount(x => x + 1)>+</button>
    </div>
    

useEffect hook

当React渲染时,hook中的函数根据依赖变化发生调用。

import useEffect, useState from 'react'
import timer from 'rxjs'


function App()
    
    const [count,setCount] = useState()
    

    useEffect(() => 
        const subscription = time(0, 1000)
        	.subscribe(() => 
                setCount( x => x + 1)
            )
				return () => 
            subscription.unsubscribe()            
        
    , [0])

    
    useEffect(() => 
        console.log("count changed to", count)
    , [count])
    return <div>
        count
    </div>

每次渲染的时候`useEffect` 都会调用,React通过判断依赖(deps)有无发生变化决定是否调用`useEffect` 中的函数。这样就将函数调用和声明式的编程统一。

去掉`useEffect` ,函数组件看上去很【纯】,像是在靠props和state渲染,加上`effect` 函数就不纯了,因为做了渲染之外的事情,比如设置定时器、打印日志、网络请求……

所以为什么叫Effect? 我们通常将计算函数返回值之外的事情都称作Effect。当这种Effect会产生负面效果,就称作Side Effect。

`useEffect` 将渲染之外的事情收敛,集中管理。

useRef hook

这个Hook让函数组件可以在多次渲染间同步引用值。 它为什么是钩子?谁触发它?其实就是每次渲染的时候触发这个hook,然后它负责在保存一个引用。

export default function LogButtonClicks() 
  const countRef = useRef(0);  

  const handle = () => 
    countRef.current++;
    console.log(`Clicked $countRef.current times`);
  ;
  console.log('rendered')

  return <button onClick=handle>Click me</button>;

利用这种机制,子组件可以向父组件同步数据:

function TextInputWithFocusButton() 
  const inputEl = useRef(null);
  const onButtonClick = () => 
    // `current` points to the mounted text input element
    inputEl.current.focus();
  ;
  return (
    <>
      <input ref=inputEl type="text" />
      <button onClick=onButtonClick>Focus the input</button>
    </>
  );

思考:`useRef` 每次渲染的时候都调用,是如何做到只初始化一次的?

例如下面这个程序:

export default function LogButtonClicks() 
  const countRef = useRef(0);   
  const [ver, setVer] = useState(0)

  const handle = () => 
    countRef.current++;
    console.log(`Clicked $countRef.current times`);
    setVer(ver => ver + 1)
  ;  

  return <button onClick=handle>Click me</button>;

React通过记录useRef的**序号**同步引用。 比如countRef是函数组件的第0个Ref,存放在位置0。第一次渲染的时候,React查看位置0中是否有值,如果没有初始化,就调用初始化函数/使用初始值。如果有,就不再初始化。

划重点:hooks本质是一种对行为的描述,不可以在任何流程控制语句中使用。

请求数据的逻辑放 useEffect 里

useMemo hook

`useMemo` hook允许我们在闭包内根据依赖缓存数据。

`useMemo` 的本质是在依赖发生变化的时候,通知React具体的VirtualDOM实例更新自己内部useMemo对应的状态。

`useMemo` 和`useHook` 非常相似,`useHook`帮助函数组件在它的多次调用间同步实例数据。

思考下面这个程序中:

function foo()
    const x = useRef(1)
    const y = useRef(2)

`x`  的值通过引用对象被保存了下来。这个引用对象在哪里? 在React的虚拟DOM对象上。`useRef` 保证了什么?

`useRef` 保证如果是:

- 相同的虚拟DOM对象(比如foo可以被多个虚拟DOM对象使用)

- 相同的位置的`useRef` (比如上面程序中x,y是不同位置的useRef)

那么会拿到相同的ref对象。这个对象可以通过`current` 属性访问,比如`x.current` `y.current`。

从这个角度看`useRef` 帮助我们在一个闭包内缓存**per instance, per location**的数据。

但是如果我们想要根据某种依赖关系更新`x` ,就需要这样做:

function foo()
    const x = useRef(1)
    
    useEffect(() => 
        // 更新x的逻辑
    , [someDeps])

如果用`useMemo` 问题就得到了简化:

function foo()
    const x = useMemo(() => 
        // 重新计算x的值      
    , [deps])

而且也不需要`x.current` 引用。 `x` 将成为一个完全由依赖推导出来的值,用户不可以随意设置。

从设计角度,`useMemo` 也在帮助缓存`per instance` `per location`  的数据,只不过增加了一个**计算依赖**,和一个**计算函数**。

这样设计的好处是什么?举一个具体的场景。

function Button()
  return <div onClick=
    useMemo(() => e => 
        console.log('onclick')
    , [])
  

上面的程序中onClick方法不会每次Button创建都被创建,相当于在`Button` 闭包内缓存了一个变量,它的值是函数。

当然, 有同学会挑战这段程序——**为了微优化降低了程序的可读性!**

说的没错! **不应该这样写!**应该让onClick的handler每次都重新创建,去消耗内存、消耗计算,来换取可读性。代码是用来读的,说到底人力成本最贵。

所以为了讲述`useMemo` 的用法,我还得再举几个真实场景的例子。

所以为了讲述`useMemo` 的用法,我还得再举几个真实场景的例子。

例子1:缓存对象

const node = useMemo<DragNode>(() => new DragNode(), [])

通过`useMemo` 语义清晰的缓存对象,替代`useRef`

例子2:实现复杂的计算逻辑

function complexComputation(a, b, c) 
   // ....


const result = useMemo(
    () =>complexComputation(a, b, c), [a,b,c])

如果有依赖明确的复杂的计算,`useMemo` 可以帮助你完成。这样在依赖`[a,b,c]` 不变动的时候,complexComputation就不会多次触发。这虽然是一个场景,可惜的是,**这样的场景是很少的**。因为前端很少有复杂的计算。甚至,当`complexComputation` 需求真的存在时,也许不用`useMemo` 更好,因为这很可能又是影响可读性的**微优化**!

例子3: 让子组件永不更新

类似skedo中父子组件的关系,它们都依赖Node类。因为Node类继承于Emitter接口,会完成`Point to Point` 的消息通知——因此在Skedo的设计中是不需要父组件渲染连带子组件渲染的。

function ParentComponent(props)
    return useMemo(() => <ChildComponent someProp=props.fooProp />, [props.someProp])


function ChildrenComponent() 
    return <div>....</div>

虽然上面这个程序很神奇,但是如果你真的有这种场景,不妨大胆使用。

useCallback hook

`useCallback` 可以看做`useMemo` 的一个语法糖。

例如:

const memoizedCallback = useCallback(
  () => 
    doSomething(a, b);
  ,
  [a, b],
)

等价于:

const memoizedCallback = useMemo(
  () => 
      return () => 
          doSomething(a, b)
      
  
, [a, b])

总体来说,`useCallback`相当于帮你省去了一级闭包。

官方文档中这样说:

`useCallback(fn, deps)`  is equivalent to `useMemo(() => fn, deps)`.

还记得之前我们`onClick`函数吗? 下面是`useCallback` 的实现:

<div onClick=useCallback(e => 
        console.log("click")
, [])

是不是看着清爽很多,~ 但是还是不建议这样做,因为没必要在牺牲可读性的基础上做性能的微优化。

`useMemo` 和`useCallback` 其实是两个低频能力。 总体来说,它们和`useRef` 能力是相似的,都是在闭包间同步一个`per virtualdom instance` `per location` 的值。类似一个静态的,基于词法作用域的缓存。

虽然我不主张用`useMemo` 和`useCallback` 做微优化。 不过作为一个框架,不能阻止用户`useMemo` 和`useCallback` 进行优化。

有这样一段程序:

function useThrottledState<T>(initialState : T, interval = 16) : [T, (val : (T|(() => T))) => void] 
	const state = useRef<T>(initialState)
	const [, setVer] = useState(0)

	const setState = useMemo(() => 
		const fn = (val : T | (() => T))  => 
			if(isFunction(val)) 
				val = val()
			
			state.current = val
			setVer(x=>x+1)
		

		return throttle(fn, interval)
	, [])

	return [state.current, setState]


这个程序存在是为了防止高频的`setState`带来的组件高频刷新,用`useThrottledState`代替`useState`。

用法如下:

const [x, setState] = useThrottledState(customData, 100)

这样setState无论调用频率如何,最终刷新频率会在每100ms一次。

1

创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖

以上是关于2-2-4 & 5 & 6 React Hooks基础API的主要内容,如果未能解决你的问题,请参考以下文章

Java优雅解决空指针问题源码级别刨析Optional

MATLAB基础1.0

C++string使用

mysql&nbsp;5.6.14&nbsp;win7&amp;nbsp…

mysql&nbsp;5.6.14&nbsp;win7&amp;nbsp…

直方图均衡化的计算以及MATLAB实现