React Hook
React Hook 有以下几个
- useState (状态)
- useEffect / useLayoutEffect (副作用)
- useContent (上下文)
- useReducer (可代替Redux)
- useMemo / useCallBack (记忆)
- useRef / useImperativeHandle (引用)
useState
用法 :
const [n,setN] = React.useState(0)
const [n,setN] = React.useState( {name : 'hht'} )
上面的代码在我们每次render时都会计算一边 初始值,我们可以将其写成一个函数,来提高运行效率
const [n, setN] = React.useState( () => 0 )
当我们在使用setN时,实际上我每次都会创建出一个新的n,因为每次setN时,都会产生一个新的作用域。
所以,为了以免将新旧n搞混,我们在使用setN时,更推荐写成函数的形式
const add = () => {
setN( i => i + 1 )
}
useReducer
useReducer其实和useState类似,但是我们可以将复杂的useState写成一个useReducer
useReducer的用法:
- 初始化一个变量
- 创建所有操作
- 在组件中引入读写接口
- 调用写接口
const inital = {n: 0, i: 1}
const reducer = (state, action) => {
if (action.type === 'add') {
return {...inital, n: action.n}
} else if (action.type === 'multi') {
return {...inital, n: action.n}
}
}
const App = () => {
const [state, dispatch] = React.useReducer(reducer, inital)
const add = () => {
dispatch({type: 'add', n: state.n + 1})
}
return (
<div>
number: n : {state.n} , i : {state.i}
<button onClick={add}>n+1</button>
</div>
)
}
useContext
通过useContext 可以创建一个局部的全局作用域
在useContext中的组件,可以通过useContext 中的value 任意调用 (vale最好写成对象形式)
const c = React.createContext(null)
const App = () => {
const [n,setN] = React.useState(0)
const add = () => {
setN( i => i +1)
}
return (
<c.Provider value={ {n,add} }>
<Child />
</c.Provider>
)
}
const Child = () => {
const {n,add} = React.useContext(c)
return (
<div>
n : {n}
<button onClick={add}>+1</button>
</div>
)
}
useEffect / useLayoutEffect
我们可以通过useEffect来模拟class组件中的生命周期。
- componentDidMount 第二个参数为 []
- componentDidUpdate 第二个参数为[xxx] (xxx 更新时调用) 或 不写第二个参数(任意变量更新时调用)
- componentWillUnmount 第一个参数中,再次返回一个函数
和useEffect不同的是,useLayoutEffect 是在 DOM ===> 外观 中间调用的
所以useLayoutEffect的调用实际比useEffect早
memo/useMemo / useCallbcak
在一个组件中,含有另一个组件的情况下,
外层的组件如果更新,那么内层的组件也会被重新渲染
useMemo就是用来解决这一问题的,当内层组件没必要重新渲染时,他不会被渲染
const App = () => {
const [n, setN] = React.useState(0)
const add = () => {
setN(i => i + 1)
}
return (
<div>
{n}
<button onClick={add}> +1</button>
<Child2/>
</div>
)
}
const Child = () => {
console.log('hi')
return (
<div>Child</div>
)
}
const Child2 = React.memo(Child)
上面的最后一行代码使用了memo,所以Child不会在随意渲染了,但是,当我们给Child传一个监听函数时,因为每次render时,新旧对象并不 === (全等)
当数组内的更新时,使用了memo的子组件(接受了peops的子组件)才会更新
这时,就需要使用到useMemo了,使用useMemo(缓存值)需要在第二个参数中,声明依赖,即哪个变量变化时,才render Child
const xxx = React.useMemo(() => {
return () => {}
}, [])
上面的代码,需要在函数中返回函数,我们可以使用useCallback(缓存函数)来进行简化
const xxx = React.useCallback( () => {}, [] )
useRef
使用React.useRef 可以实现类似于useState的功能,并且不像useState那样每次更新变量,都会产生一个新的变量。
React.useRef还可以实现获取DOM
- 更新同一个变量
const App = () => {
const divRef = React.useRef(100)
const [_n, _setN] = React.useState(null)
const add = () => {
divRef.current += 1
_setN(divRef.current)
}
return (
<div>
{divRef.current}
<button onClick={add}>+1</button>
</div>
)
}
因为useRef 生成了一个变量,所以即使每次都重新渲染,不同的divRef都会指向同一个地址。
同时,更新ref,并不会刷新视图,所以我们需要一个useState来辅助进行视图的刷新。
- 获取DOM
在class组件中,我们可以通过React.useRef给另一个组件传递一个ref值,并在父组件中获取到他。
class App extends React.PureComponent {
render() {
const divRef = React.createRef()
return (
<div>
<Child ref1={divRef}/>
<button
onClick={() => {
console.log(divRef.current)
}}>log
</button>
</div>
);
}
}
class Child extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
return (
<div ref={this.props.ref1}>123</div>
)
}
}
在函数组件中,上面的方法就不行了,我们需要通过forwardRef实现
const App = () => {
const divRef = React.useRef(null)
return (
<div>
<Child2 ref1={divRef}/>
<button
onClick={() => {
console.log(divRef.current)
}}>log
</button>
</div>
)
}
const Child = (props, ref1) => {
return (
<div ref={props.ref1}>child</div>
)
}
const Child2 = React.forwardRef(Child)