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的主要内容,如果未能解决你的问题,请参考以下文章
mysql 5.6.14 win7&nbsp…