<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
1. RN hooks 用法:
(1) const [num, setNum] = useState(()=>{
const num1 = 1 + 2
const num2 = 2 + 3
return num1 + num2
})
(2) 独立值: const [num, setNum] = useState(2)
2.setNum 函数用法: setNum((pre)=>pre + 1)
3.useEffect 副作用函数(可以额外处理一些功能) 不支持async await,可以放计时器或者请求数据 等异步逻辑
(1) useEffect(()=>{
getApiData() // 请求接口数据
}, []) // 依赖变化的值,一般是状态值
(2) useEffect 中清理计时器
useEffect(()=>{
const timer = setInterval(()=>{
}, 1000)
return () =>{
clearInterval(timer) // 清理计时器
}
})
(3) useLayoutEffect: 等effect 函数中内容执行完后再渲染, 好处页面会不闪动,坏处时间过长有可能阻塞页面渲染
(4) useReducer 场景: 在修改状态值之前要执行一些固定逻辑
import { Reducer, useReducer} from 'react'
// 固定逻辑函数
const reducer = (state, action) =>{
switch(action.type) {
case 'add':
return {
result: state.result + action.num // 只有类似返回新对象才能触发重新渲染(state.result +=action.num return state 无法重新渲染)
}
case 'minus':
return {
result: state.result - action.num
}
}
return state;
}
const [res, dispatch] = useReducer(reducer, { result:0}) // (1.函数, 初始值)
注意触发函数: dispatch({ type: 'minus', num: 1 }) 结果值 res.result
(4) 总是返回新对象,性能不好,复杂对象就要用到immer库了 npm install --save immer
return produce(state, (state)=>{state.a.c.e + = action.num}) // 将state 中的a对象中的 c对象中的 e 属性加上 action 中num的值
immer 是依赖 Proxy 实现的,它会监听你在函数里对属性的修改,然后帮你创建一个新对象
useState 中 onClick={() => {
obj.a.c.e ++;
setObj(obj); // 这样也不会触发重新渲染, 因为对象引用未变
}}
总结: react 中 只要涉及到state状态值的修改,必须返回新的对象, 不管是useState 还是useReducer
如果是复杂深层对象的修改,可以用immer 来优化, 为啥react推崇的数据不可变呢就是这个原因
4. useRef 场景: 里面有current属性引用, 用来保存dom的引用或者别的内容,但是它不会触发重新渲染
想要重新渲染, 可以配合useState(0) 进行重新设置一个任意值
5.单个组件拿到ref 直接使用useRef, 那么想把ref从子组件传递到父组件呢? forwardRef + useImperativeHandle
const children =forwardRef((props, ref)=>{
// 暴露给父组件的方法
useImperativeHandle(ref, ()=>{
return {
aaa(){
inputRef.current?.focus()
}
}
}, [inputRef])
const inputRef = useRef(null)
return (
<input ref={inputRef}>
</input>
)
})
const components = ((props)=>{
const childrenRef = useRef(null)
useEffect(()=>{
// 父组件中调用子组件的方法
childrenRef?.current?.aaa()
})
return <div>
{{
<children ref={}>
}}
</div>
})
6.useContext: 跨任意层级组件传递数据, 一般用context, 配置数据基本都用context传递
组件A -----> context ---> 组件B
import { createContext, useContext } from 'react'
const countContext =createContext(111) // 创建
const AAA = ()=>{
return (
<countContext.Provider value = {222}> // 修改值
<BBB></BBB>
</countContext.Provider>
)
}
const BBB = () =>{
return (
<div>
<CCC></CCC>
</div>
)
}
const CCC = ()=>{
const count = useContext(countContext) //useContext 取出来使用
return <div>{count}</div>
}
7.memo + useMemo + useCallback
(1)
const AAA = () =>{
count [, setNum] = useState(1)
useEffect (()=>{
setInerval(()=>{
setNum(Math.random())
})
}, [])
return(
<BBB count ={2}>
</BBB>
)
}
const BBB = (props)=>{
console.log('会执行几次?')
return <h2>{
props?.count
}</h2>
}
注意: 每两秒都会打印一次, 也就是每次都会触发BBB 组件的重新渲染, 很明显组件B 并不需要重新渲染, 这时候可以加上memo
import {memo} from react
const MemoBBB = memo(BBB)
const AAA = ()=>{
return(<MemoBBB count ={2}></MemoBBB>)
}
总结: memo的作用是只有props变的时候,才会重新渲染包裹的组件
(2) memo 是防止props没变化时的重新渲染, useMemo 和 useCallback 是防止 props的不必要变化
const BBBcallback = ()=>{
}
<MemeBBB count={count} callback = {BBBcallback}></MemoBBB> 你会发现memo失效了,因为函数每次都是新创建的, 这样就是需要useCallback了
const BBBcallback = useCallback(()=>{
}, []) // 当deps数组没有变化时候, 都返回同一个函数, 只有当deps数组变化时候, 才把函数设置为新传的函数, 相当于函数缓存
同样useMemo 也是和memo打配合, 只不过它保存的不是函数, 而是值
const count2 = useMemo(()=>{
return count * 10
}, [count])
const bbbCallback = useCallback(()=>{
console.log('进行函数缓存!')
}, [])
<MemoBBB count={count2} callback={bbbCallback}></MemoBBB>
总结: memo 使用总是需要 useMemo & useCallback 来帮忙, 但是 useMemo & useCallback 不仅仅帮助memo
*/
</script>
</body>
</html>