React Hook 指南 useState() useEffect() useReducer() 自定义 Hook

本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/766531

相关文章

Python正则表达式匹配和替换的操作指南

《Python正则表达式匹配和替换的操作指南》正则表达式是处理文本的强大工具,Python通过re模块提供了完整的正则表达式功能,本文将通过代码示例详细介绍Python中的正则匹配和替换操作,需要的朋... 目录基础语法导入re模块基本元字符常用匹配方法1. re.match() - 从字符串开头匹配2.

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资