本文主要是介绍React Hook 指南 useState() useEffect() useReducer() 自定义 Hook,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- useState()状态管理
- setState 和 useState 的区别
- setState 和 useState 同样场景的应用
- 如何让 Function Component 也打印 3 3 3
- 借助一个新 Hook - useRef 的能力
- 如何不改造原始值也打印 3 3 3
- useState() 的无效调用
- useEffect()状态管理
- useEfffect第二个参数不能使用引用类型
- 复杂状态用useReducer
- 自定义 Hook
useState()状态管理
import React, { usestate } from 'react" ;function Bulbs() {
const [setOn] = useState( false) ; //设置初始值为false
const lightOn= ()=>set0n( true ); //改变初始值
const lightOff =()=> setOn( false );
return (
<div className={on ? 'bulb-on' : 'bulb-off '}/>
<button onclick={lightOn}>开灯</ button>
<button onclick={light0ff}>关灯</button></>
);}
setState 和 useState 的区别
1.获取修改后的值
setState
this.state={
count:0
}
this.setState({count: this.state.this.state=count + 1
}, () => {console.log(this.state.count); // 这里是监控到的最新值
})
useState
const [ count, setCount ] = useState(0);
setCount(1);
useEffect(() => {console.log(count); // 这里是监控到的最新值
}, [ count ]);
setState 和 useState 同样场景的应用
setState
class Counter extends Component {state = { count: 0 };log = () => {this.setState({count: this.state.count + 1});setTimeout(() => {console.log(this.state.count);}, 3000);};render() {return (<div><p>You clicked {this.state.count} times</p><button onClick={this.log}>Click me</button></div>);}
}
// 3 3 3
首先 state 是 Immutable 的,setState 后一定会生成一个全新的 state 引用。
但 Class Component 通过 this.state 方式读取 state,这导致了每次代码执行都会拿到最新的 state 引用,所以快速点击三次的结果是 3 3 3。
useState
function Counter() {const [count, setCount] = useState(0);const log = () => {setCount(count + 1);setTimeout(() => {console.log(count);}, 3000);};return (<div><p>You clicked {count} times</p><button onClick={log}>Click me</button></div>);
}
// 0 1 2
useState 产生的数据也是 Immutable 的,通过数组第二个参数 Set 一个新值后,原来的值会形成一个新的引用在下次渲染时。
但由于对 state 的读取没有通过 this. 的方式,使得 每次 setTimeout 都读取了当时渲染闭包环境的数据,虽然最新的值跟着最新的渲染变了,但旧的渲染里,状态依然是旧值。
如何让 Function Component 也打印 3 3 3
借助一个新 Hook - useRef 的能力
function Counter() {const count = useRef(0);const log = () => {count.current++;setTimeout(() => {console.log(count.current);}, 3000);};return (<div><p>You clicked {count.current} times</p><button onClick={log}>Click me</button></div>);
}
useRef 的功能:通过 useRef 创建的对象,其值只有一份,而且在所有 Rerender 之间共享。
count.current 赋值或读取,读到的永远是其最新值,而与渲染闭包无关,因此如果快速点击三下,必定会返回 3 3 3 的结果。
但这种方案有个问题,就是使用 useRef 替代了 useState 创建值.
如何不改造原始值也打印 3 3 3
function Counter() {const [count, setCount] = useState(0);const currentCount = useRef(count);useEffect(() => {currentCount.current = count;});const log = () => {setCount(count + 1);setTimeout(() => {console.log(currentCount.current);}, 3000);};return (<div><p>You clicked {count} times</p><button onClick={log}>Click me</button></div>);
}
一种最简单的做法,就是新建一个 useRef 的值给 setTimeout 使用,而程序其余部分还是用原始的 count
useState() 的无效调用
在条件中调用useState()是不正确的:
function Switch({ isSwitchEnabled }) {if (isSwitchEnabled) {// Badconst [on, setOn] = useState(false);}// ...
}
在嵌套函数中调用useState()也是不对的
function Switch( ) {let on = false;let setOn = ( ) =→> {};function enableSwitch() {[ on,setOn] = usestate(false);}
return (
<button onclick=fenableSwitch}>Enable light switch state
</button>
);
}
useEffect()状态管理
function Counter() {const [count, setCount] = useState(0);const currentCount = useRef(count);useEffect(() => {currentCount.current = count;});const log = () => {setCount(count + 1);setTimeout(() => {console.log(currentCount.current);}, 3000);};return (<div><p>You clicked {count} times</p><button onClick={log}>Click me</button></div>);
}
// 3 3 3
useEffect 会在第一次渲染之后和每次更新之后都会执行。
相当于componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合
useEffect 是处理副作用的,其执行时机在 每次 Render 渲染完毕后,换句话说就是每次渲染都会执行,只是实际在真实 DOM 操作完毕后。
我们可以利用这个特性,在每次渲染完毕后,将 count 此时最新的值赋给 currentCount.current,这样就使 currentCount 的值自动同步了 count 的最新值。
要注意的是,useEffect 也随着每次渲染而不同的,同一个组件不同渲染之间,useEffect
内闭包环境完全独立。对于本次的例子,useEffect 共执行了 四次,经历了如下四次赋值最终变成 3:
setTimeout 的例子,三次点击触发了四次渲染,但 setTimeout 分别生效在第 1、2、3 次渲染中,因此值是 0 1 2。
useEffect 的例子中,三次点击也触发了四次渲染,但 useEffect 分别生效在第 1、2、3、4 次渲染中,最终使 currentCount 的值变成 3。
useEfffect第二个参数不能使用引用类型
useEffect接受两个参数
-
第一个参数是函数(这里叫effect函数),它的作用是,在页面渲染后执行这个函数。因此你可以把
ajax请求等放在这里执行; -
第二个参数是一个数组,这里注意:
import React, { useState, useEffect } from 'react';function Example() {const [count, setCount] = useState({});useEffect(() => {setCount({test:"count是一个对象,使得页面死循环"})},[count]);return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}
//死循环
是因为在JavaScript中,{} === {}结果是false,{a:1} === {a:1}同样,由此造成了react以为两个值不同,就一直的渲染最终页面死循环
使用useEffect()时,有一点需要注意。如果有多个副效应,应该调用多个useEffect(),而不应该合并写在一起。
复杂状态用useReducer
用法 => const [state, dispatch] = useReducer(reducer, initialState);
function reducer(state, action) {switch (action.type) {case "increment":return {...state,count: state.count + 1};default:return state;}
}
这样就可以通过调用 dispatch({ type: ‘increment’ }) 的方式实现 count 自增了。
假设需要编写一个最喜欢的电影列表。用户可以添加电影,也可以删除已有的电影
reducer管理电影的状态,有两种操作类型:
"add"将新电影插入列表
"remove"从列表中按索引删除电影
import React, { useReducer } from 'react ' ;function reducer( state,action) {
switch (action.type) {
case 'add ' :
return [...state,action.item];case ' remove ' :
return [
...state.slice( 0,action.index ),...state.slice( action.index + 1)];
default:
throw new Error( );
}
}
function FavoriteMovies( ) {
const [state,dispatch] = useReducer( reducer,[{ name: 'Heat' }]
return (
);
自定义 Hook
是不是觉得每次都写一堆 useEffect 同步数据到 useRef 很烦?是的,想要简化,就需要引出一个新的概念:自定义 Hooks。
首先介绍一下,自定义 Hooks 允许创建自定义 Hook,只要函数名遵循以 use 开头,且返回非 JSX 元素,就是 Hooks 啦!自定义 Hooks 内还可以调用包括内置 Hooks 在内的所有自定义 Hooks。
function useCurrentValue(value) {const ref = useRef(0);useEffect(() => {ref.current = value;}, [value]);return ref;
}
useEffect 的第二个参数,dependences。dependences 这个参数定义了 useEffect 的依赖,在新的渲染中,只要所有依赖项的引用都不发生变化,useEffect 就不会被执行,且当依赖项为 [] 时,useEffect 仅在初始化执行一次,后续的 Rerender 永远也不会被执行。
function Counter() {const [count, setCount] = useState(0);const currentCount = useCurrentValue(count);const log = () => {setCount(count + 1);setTimeout(() => {console.log(currentCount.current);}, 3000);};return (<div><p>You clicked {count} times</p><button onClick={log}>Click me</button></div>);
}
// 3 3 3
//这个例子中,我们告诉 React:仅当 value 的值变化了,再将其最新值同步给 ref.current。
eslint-plugin-react-hooks
这个插件,会自动订正你的代码中的依赖,想不对依赖诚实都不行!
这篇关于React Hook 指南 useState() useEffect() useReducer() 自定义 Hook的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!