React学习笔记(四)—— 组件通信与状态管理、Hooks、Redux、Mobx
<p>react管理状态的工具:</p><p>1、利用hooks进行状态管理;</p>
<p>2、利用Redux进行状态管理,这种方式的配套工具比较齐全,可以自定义各种中间件;</p>
<p>3、利用Mobx进行状态管理,它通过透明的函数响应式编程使得状态管理变得简单和可扩展。</p>
<p>2013 年 5 月 React 诞生。但 2015 年之前,大概都是 jQuery 的天下。2015 年 3 月 React 0.13.0 发布,带来了 class 组件写法。</p>
<p>在 React class 组件时代,状态就是 this.state,使用 this.setState 更新。</p>
<p>为避免一团乱麻,React 引入了 "组件" 和 "单向数据流" 的理念。有了状态与组件,自然就有了状态在组件间的传递,一般称为 "通信"。</p>
<p>父子通信较简单,而深层级、远距离组件的通信,则依赖于 "状态提升" + props 层层传递。</p>
<p>于是,React 引入了 Context,一个用于解决组件 "跨级" 通信的官方方案。</p>
<p>但 Context 其实相当于 "状态提升",并没有额外的性能优化,且写起来比较啰嗦。</p>
<p>为优化性能,一般会添加多个 Context,写起来就更啰嗦。在项目没那么复杂时,还不如层层传递简单。</p>
<p>Context 没那么好用,React 官方也没什么最佳实践,于是一个个社区库就诞生了。</p>
<p>目前比较常用的状态管理方式有hooks、redux、mobx三种。</p>
<h1>一、组件通信</h1>
<p><strong>(1).组件的特点</strong></p>
<p>组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据</p>
<p>在组件化过程中,通常会将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能</p>
<p><strong>(2).知道组件通讯意义</strong></p>
<p>而在这个过程中,多个组件之间不可避免的要共享某些数据</p>
<p>为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通、这个过程就是组件通讯</p>
<h2>1.1、父传子</h2>
<p>父组件向子组件通信是通过父组件的props传递数据完成。</p>
<p>UserList.jsx接收父组件的数据,展示用户信息,子组件:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserList extends Component {
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<ul><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.users.map(user =><li key={user.id}><span style="color: rgba(0, 0, 0, 1)">
{user.name}
</span></li>)}
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>UserListContainer.jsx向子组件传递数据,父组件:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import UserList from </span>'./UserList'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserListContainer extends Component {
state</span>=<span style="color: rgba(0, 0, 0, 1)">{users:[]}
componentDidMount(){
const users</span>=<span style="color: rgba(0, 0, 0, 1)">[
{id:</span>"1001",name:"Jone"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1002",name:"Mali"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1003",name:"Locy"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1004",name:"Rose"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1005",name:"Jack"<span style="color: rgba(0, 0, 0, 1)">}
]
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({users:users});
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div><div>用户信息列表</div>
<UserList users={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users}/>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321113930805-83851443.png" alt="" width="502" height="326" loading="lazy"></p>
<p> 解释:数据users在父组件中通过属性传递给子组件UserList,在UserList中通过props接收父组件传入的数据,完成父传子,这是最简单,最基本的一个状态的传递方法,推荐常用。</p>
<h2>1.2、子传父</h2>
<p>子传父依然使用props,父组件先给子组件传递一个回调函数,子组件调用父组件的回调函数传入数据,父组件处理数据即可。</p>
<p>在UserList中添加新增加功能:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserList extends Component {
state</span>={newUser:""<span style="color: rgba(0, 0, 0, 1)">}
handleChange</span>=e=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({newUser:e.target.value});
}
handleClick</span>=e=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 0, 255, 1)">this</span>.state.newUser&&<span style="color: rgba(0, 0, 255, 1)">this</span>.state.newUser.length>0<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.onAddUser(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.newUser);
}
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<ul><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.users.map(user =><li key={user.id}><span style="color: rgba(0, 0, 0, 1)">
{user.name}
</span></li>)}
</ul>
<div><span style="color: rgba(0, 0, 0, 1)">
姓名:</span><input type="text" onChange={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleChange} value={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.newUser}></input>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick} type="submit">新增</button>
</div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>在UserListContainer中添加onAddUser参数与函数:</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import UserList from </span>'./UserList'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserListContainer extends Component {
state</span>=<span style="color: rgba(0, 0, 0, 1)">{users:[]}
componentDidMount(){
const users</span>=<span style="color: rgba(0, 0, 0, 1)">[
{id:</span>"1001",name:"Jone"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1002",name:"Mali"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1003",name:"Locy"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1004",name:"Rose"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1005",name:"Jack"<span style="color: rgba(0, 0, 0, 1)">}
]
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({users:users});
}
onAddUser(newUser){
let users</span>=<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.users;
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({users:users.concat({
id:parseInt((users.id)+1)+""<span style="color: rgba(0, 0, 0, 1)">,
name:newUser
})});
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div><div>用户信息列表</div>
<UserList users={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users} onAddUser={<span style="color: rgba(0, 0, 255, 1)">this</span>.onAddUser.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)}/>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>运行:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321115328573-481595968.png" alt="" width="572" height="330" loading="lazy"></p>
<p> </p>
<p> 解释:在子组件中用户输入了一个新的姓名,调用props.addUser方法将新添加的用户信息发送给父组件完成添加功能,所以这里实现了子传父功能。</p>
<div>
<div> * UserListContainer中包含UserList组件,所以UserListContainer是父组件,而UserList是子组件</div>
<div> * 子组件通过调用父组件中的onAddUser方法将输入的用户添加到集合中,完成子传父功能</div>
</div>
<h2>1.3、兄弟组件间通信</h2>
<p>兄弟组件不能直接相互传送数据,需要通过状态提升的方式实现兄弟组件的通信,即把组件之间需要共享的状态保存到距离它们最近的共同父组件内,任意一个兄弟组件都可以通过父组件传递的回调函数来修改共享状态,父组件中共享状态的变化也会通过props向下传递给所有兄弟组件,从而完成兄弟组件之间的通信。</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321141925966-1557719760.png" alt="" width="446" height="309" loading="lazy"></p>
<p> </p>
<p> 我们在UserListContainer中新增一个子组件UserDetail,用于显示当前选中用户的详细信息,比如用户的年龄、联系方式、家庭地址等。这时,UserList 和 UserDetail 就成了兄弟组件,UserListContainer是它们的共同父组件。当用户在 UserList中点击一条用户信息时,UserDetail需要同步显示该用户的详细信息,因此,可以把当前选中的用户 currentUser保存到UserListContainer的状态中。</p>
<p>UserList.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import </span>"./css/userList.css"
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 用户列表组件
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserList extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 构造函数
* @param {*} props
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
constructor(props) {
super(props);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.state = { newUser: ""<span style="color: rgba(0, 0, 0, 1)">};
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 输入框内容变化事件
* @param {*} e
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
handleChange </span>= e =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({ newUser: e.target.value });
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 新增用户按钮点击事件
* @param {*} e
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
handleClick </span>= e =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.state.newUser && <span style="color: rgba(0, 0, 255, 1)">this</span>.state.newUser.length > 0<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.onAddUser(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.newUser);
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 用户列表项点击事件
* @param {*} userId
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
handleSelect(userId) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.onSetCurrentUser(userId);
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 渲染函数
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<ul><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.users.map(user => <li key={user.id} className={user.id === <span style="color: rgba(0, 0, 255, 1)">this</span>.props.currentUserId ? "active" : ""} onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSelect.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>, user.id)}><span style="color: rgba(0, 0, 0, 1)">
{user.name}
</span></li>)}
</ul>
<div><span style="color: rgba(0, 0, 0, 1)">
姓名:</span><input type="text" onChange={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleChange} value={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.newUser}></input>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick} type="submit">新增</button>
</div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>UserDetails.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserDetails extends Component {
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.currentUser?
<div>
<h2>详细信息</h2>
<fieldset>
<legend>用户</legend>
<p><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> 用户编号 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">}
编号:{</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.currentUser.id}
</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> 用户姓名 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">}
姓名:{</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.currentUser.name}
</span></p>
</fieldset>
</div>:"" )
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>UserListContainer.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import UserList from </span>'./UserList'<span style="color: rgba(0, 0, 0, 1)">
import UserDetails from </span>'./UserDetails'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserListContainer extends Component {
state</span>={users:[],currentUserId:<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">}
componentDidMount(){
const users</span>=<span style="color: rgba(0, 0, 0, 1)">[
{id:</span>"1001",name:"Jone"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1002",name:"Mali"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1003",name:"Locy"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1004",name:"Rose"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1005",name:"Jack"<span style="color: rgba(0, 0, 0, 1)">}
]
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({users:users});
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加用户</span>
<span style="color: rgba(0, 0, 0, 1)"> onAddUser(username){
let users</span>=<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.users;
let newUser</span>=<span style="color: rgba(0, 0, 0, 1)">{
id:(parseInt(users.id)+1)+""<span style="color: rgba(0, 0, 0, 1)">,
name:username
};
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({users:users.concat(newUser),currentUserId:newUser.id});
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置当前用户</span>
<span style="color: rgba(0, 0, 0, 1)"> onSetCurrentUser(userId){
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({currentUserId:userId});
}
render() {
const users</span>=<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users.filter(user=>user.id===<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.currentUserId);
const currentUser</span>=users&&users.length>0&&users;
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div><div>用户信息列表</div>
<UserList users={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users} onAddUser={<span style="color: rgba(0, 0, 255, 1)">this</span>.onAddUser.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)} onSetCurrentUser={<span style="color: rgba(0, 0, 255, 1)">this</span>.onSetCurrentUser.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)} currentUserId={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.currentUserId}/>
<UserDetails currentUser={currentUser} />
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321151507082-1976293588.png" alt="" width="868" height="587" loading="lazy"></p>
<p>解释:在子组件UserList中添加一个username,通过onAddUser将username传入父组件UserListContainer中,这里完成了状态提升,在UserListContainer中再将新添加的用户传入给UserDetail组件,实现从父传给子组件,整个过程实现了兄弟之间的数据传递功能。</p>
<p>UserListPro.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import </span>"./css/userListPro.css"<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserListPro extends Component {
state</span>={username:""<span style="color: rgba(0, 0, 0, 1)">}
usernameChange</span>=(e)=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({username:e.target.value})
}
handleSubmit</span>=e=><span style="color: rgba(0, 0, 0, 1)">{
const {username} </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state;
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(username&&username.length>0<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用父组件的onAddUser方法将值username传递给父组件</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.props.onAddUser(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.username);
}
e.preventDefault();
};
selectHandle</span>=(e)=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将当前选择的用户编号传给父组件</span>
<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.onSetCurrentId(e.target.id);
};
render() {
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<ul className='userDetail'><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.users.map(user=><<span style="color: rgba(0, 0, 0, 1)">li
key</span>=<span style="color: rgba(0, 0, 0, 1)">{user.id}
id</span>=<span style="color: rgba(0, 0, 0, 1)">{user.id}
onClick</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.selectHandle}
className</span>={user.id===<span style="color: rgba(0, 0, 255, 1)">this</span>.props.currentId?"active":""<span style="color: rgba(0, 0, 0, 1)">}
</span>>{user.id} - {user.name}</li>)}
</ul>
<div>
<form onSubmit={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleSubmit}>
<p>
<label>姓名:</label>
<input type="text" value={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.username} onChange={<span style="color: rgba(0, 0, 255, 1)">this</span>.usernameChange}/>
<button>添加</button>
</p>
</form>
</div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>UserListContainer.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import UserListPro from </span>'./UserListPro'<span style="color: rgba(0, 0, 0, 1)">;
import UserDetail from </span>'./UserDetail'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* UserListContainer中包含UserList组件,所以UserListContainer是父组件,而UserList是子组件
* 子组件通过调用父组件中的onAddUser方法将输入的用户添加到集合中,完成子传父功能
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserListContainer extends Component {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">currentId用于记录当前用户的编号</span>
state={users:[],currentId:<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">}
componentDidMount(){
const users</span>=<span style="color: rgba(0, 0, 0, 1)">[
{id:</span>"1001",name:"Jone"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1002",name:"Mali"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1003",name:"Locy"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1004",name:"Rose"<span style="color: rgba(0, 0, 0, 1)">},
{id:</span>"1005",name:"Jack"<span style="color: rgba(0, 0, 0, 1)">}
];
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({users:users});
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">新添加用户</span>
<span style="color: rgba(0, 0, 0, 1)"> onAddUser(username){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成新的编号</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">let id=parseInt(this.state.users.id)+1;</span>
let id=<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users[<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users.length-1].id*1+1<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将添加的新用户</span>
const user=<span style="color: rgba(0, 0, 0, 1)">{id,name:username};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将新用户添加到users状态中</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.setState({users:<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.users.concat(user),currentId:id});
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子组件通过该方法设置当前用户的编号</span>
onSetCurrentId=(id)=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({currentId:id});
}
render() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">根据用户编号从用户集合中获取用户集合</span>
const users=<span style="color: rgba(0, 0, 255, 1)">this</span>.state.users.filter(user => user.id === <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.currentId);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当前用户</span>
let currentUser=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果查找了</span>
<span style="color: rgba(0, 0, 255, 1)">if</span>(users&&users.length>0<span style="color: rgba(0, 0, 0, 1)">){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置当前用户</span>
currentUser=users;
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>用户列表</h2>
<<span style="color: rgba(0, 0, 0, 1)">UserListPro
users</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.users}
onAddUser</span>={<span style="color: rgba(0, 0, 255, 1)">this</span>.onAddUser.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)}
currentId</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.currentId}
onSetCurrentId</span>={<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.onSetCurrentId}
</span>/>
<div>
<UserDetail currentUser={currentUser}></UserDetail>
</div>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>UserDetail.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class UserDetail extends Component {
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.props.currentUser?
<div>
<h2>用户详情</h2>
<fieldset>
<legend>详细</legend>
<p><span style="color: rgba(0, 0, 0, 1)">
编号:{</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.currentUser.id}
</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">
姓名:{</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.currentUser.name}
</span></p>
</fieldset>
</div>
:""<span style="color: rgba(0, 0, 0, 1)">
)
}
}</span></pre>
</div>
<p>css/userListPro.css</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">.userDetail li</span>{<span style="color: rgba(255, 0, 0, 1)">
cursor</span>:<span style="color: rgba(0, 0, 255, 1)"> pointer</span>;
}<span style="color: rgba(128, 0, 0, 1)">
.userDetail li:hover</span>{<span style="color: rgba(255, 0, 0, 1)">
background</span>:<span style="color: rgba(0, 0, 255, 1)"> lightyellow</span>;
}<span style="color: rgba(128, 0, 0, 1)">
.active</span>{<span style="color: rgba(255, 0, 0, 1)">
background</span>:<span style="color: rgba(0, 0, 255, 1)"> lightyellow</span>;
}</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230323104824491-1380894577.png" alt="" width="624" height="631" loading="lazy"></p>
<h2>1.4、多级组件通信</h2>
<p>当组件所处层级太深时,往往需要经过很层的props传递才能将所需的数据或者回调函数传递给使用组件,所以props作为桥梁通信便会显得很麻烦。React提供了一个context上下文,让任意层级的子组件都可以获取父组件中的状态和方法。</p>
<p>Parent.jsx 父</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import Sub1 from </span>'./Sub1'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Parent extends Component {
state</span>=<span style="color: rgba(0, 0, 0, 1)">{
n:</span>100<span style="color: rgba(0, 0, 0, 1)">
}
setN(n){
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({n});
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{backgroundColor:"lightblue"}}>
<ul>
<li>
<div>
<h2>父组件 n={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}</h2>
</div>
<Sub1 onSetN={<span style="color: rgba(0, 0, 255, 1)">this</span>.setN.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)}></Sub1>
</li>
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>Sub1.jsx 子</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import Sub11 from </span>'./Sub11'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Sub1 extends Component {
setNumber() {
let n</span>=parseInt(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtInput.value);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.onSetN(n);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{background:"lightred"}}>
<ul>
<li>
<h2>子组件:Sub1</h2>
<p>
<input ref={input=><span style="color: rgba(0, 0, 255, 1)">this</span>.txtInput=input} type="text" />
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.setNumber.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)}>设置N的值</button>
</p>
<Sub11 onSetN={<span style="color: rgba(0, 0, 255, 1)">this</span>.props.onSetN}/>
</li>
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>Sub11.jsx 孙</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Sub11 extends Component {
setNumber() {
let n</span>=parseInt(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtInput.value);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.props.onSetN(n);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{background:"lightred"}}>
<ul>
<li>
<h2>孙组件:Sub11</h2>
<p>
<select ref={input=><span style="color: rgba(0, 0, 255, 1)">this</span>.txtInput=input} type="text">
<option value={300}>300</option>
<option value={600}>600</option>
<option value={900}>900</option>
</select>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.setNumber.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)}>设置N的值</button>
</p>
</li>
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321161710584-773713861.png" alt="" loading="lazy"></p>
<p> 解释:Parent组件将setN传递给Sub1,Sub1再传递给Sub11,Sub11调用SetN设置父组件的值,这样做可以实现组件间的多级传递,但非常麻烦,如果层级较多时。</p>
<h2>1.5、Context</h2>
<p>当组件所处层级太深时,往往需要经过很层的props传递才能将所需的数据或者回调函数传递给使用组件,所以props作为桥梁通信便会显得很麻烦。React提供了一个context上下文,让任意层级的子组件都可以获取父组件中的状态和方法。</p>
<p>每个组件都拥有context属性,可以查看到:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321161954341-1972570711.png" alt="" loading="lazy"></p>
<p>getChildContext:与访问context属性需要通过contextTypes指定可访问的属性一样,getChildContext指定的传递给子组件的属性需要先通过childContextTypes来执行,不然会报错。</p>
<p>使用context改进后的示例如下:</p>
<p>Parent.jsx 父</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import {PropTypes} from </span>'prop-types'<span style="color: rgba(0, 0, 0, 1)">
import Sub1 from </span>'./Sub1'<span style="color: rgba(0, 0, 0, 1)">;
class Parent extends Component {
state</span>=<span style="color: rgba(0, 0, 0, 1)">{
n:</span>100<span style="color: rgba(0, 0, 0, 1)">
}
setN</span>=(n)=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({n});
}
getChildContext(){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> {onSetN: <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setN}
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{backgroundColor:"lightblue"}}>
<ul>
<li>
<div>
<h2>父组件 n={<span style="color: rgba(0, 0, 255, 1)">this</span>.state.n}</h2>
</div>
<Sub1></Sub1>
</li>
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">声明context的属性的类型信息</span>
Parent.childContextTypes =<span style="color: rgba(0, 0, 0, 1)"> {
onSetN:PropTypes.func
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Parent;</pre>
</div>
<p>Sub1.jsx 子</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import Sub11 from </span>'./Sub11'<span style="color: rgba(0, 0, 0, 1)">;
import {PropTypes} from </span>'prop-types'<span style="color: rgba(0, 0, 0, 1)">
class Sub1 extends Component {
setNumber() {
let n</span>=parseInt(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtInput.value);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.context.onSetN(n);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{background:"lightred"}}>
<ul>
<li>
<h2>子组件:Sub1</h2>
<p>
<input ref={input=><span style="color: rgba(0, 0, 255, 1)">this</span>.txtInput=input} type="text" />
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.setNumber.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)}>设置N的值</button>
</p>
<Sub11/>
</li>
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">声明要使用的context属性的类型信息</span>
Sub1.contextTypes=<span style="color: rgba(0, 0, 0, 1)">{
onSetN: PropTypes.func
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Sub1;</pre>
</div>
<p>Sub11.jsx 孙</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import {PropTypes} from </span>'prop-types'<span style="color: rgba(0, 0, 0, 1)">
class Sub11 extends Component {
setNumber() {
let n</span>=parseInt(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.txtInput.value);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.context.onSetN(n);
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{background:"lightred"}}>
<ul>
<li>
<h2>孙组件:Sub11</h2>
<p>
<select ref={input=><span style="color: rgba(0, 0, 255, 1)">this</span>.txtInput=input} type="text">
<option value={300}>300</option>
<option value={600}>600</option>
<option value={900}>900</option>
</select>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.setNumber.bind(<span style="color: rgba(0, 0, 255, 1)">this</span>)}>设置N的值</button>
</p>
</li>
</ul>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">声明要使用的context属性的类型信息</span>
Sub11.contextTypes=<span style="color: rgba(0, 0, 0, 1)">{
onSetN: PropTypes.func
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Sub11;</pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230321163426715-1747494835.png" alt="" loading="lazy"></p>
<p>解释:父组件通过getChildContext向自己的后代组件传递一个对象,使用childContextTypes声明属性的类型,在后代组件中使用this.context接收父组件传入的对象,不过所有的要使用父组件中传入对象的子组件都要声明contextTypes对象的属性类型信息。这样就实现了一次下发,多处使用,完成了组件间数据的传递。</p>
<h1>二、Hooks</h1>
<h2> 2.1、解构</h2>
<p>1、数组解构就是能快速提取数组中的指定成员(数组的某一项值或所有的值)</p>
<p>例如:</p>
<p>解构赋值都是一一对应的,按照顺序。</p>
<div class="cnblogs_code">
<pre>const arr =
const </span>=<span style="color: rgba(0, 0, 0, 1)"> arr
console.log(a,b,c) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 200,300,400</span></pre>
</div>
<p>也可以取数组的某一项值(结构必须保持一致)</p>
<div class="cnblogs_code">
<pre>const arr =
const [, , c] </span>=<span style="color: rgba(0, 0, 0, 1)"> arr
console.log(c) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 400</span></pre>
</div>
<p>还可在用“...”的方式提取所有的成员(注意的是这种...的写法只能在解构成员的最后一个成员使用)代码如下</p>
<div class="cnblogs_code">
<pre>const arr =
const </span>=<span style="color: rgba(0, 0, 0, 1)"> arr
console.log(all) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 会返回得到一个最后所有的数组</span></pre>
</div>
<p>如果提取的解构成员小于数组的长度,就会从前到后的顺序来提取,代码如</p>
<div class="cnblogs_code">
<pre>const arr =
const </span>=<span style="color: rgba(0, 0, 0, 1)"> arr
console.log(a) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 200 按顺序提取第一个</span></pre>
</div>
<p>如果提取成员大于数组长度,那么最后的提取的最后是undefined,代码如下</p>
<div class="cnblogs_code">
<pre>const arr =
const </span>=<span style="color: rgba(0, 0, 0, 1)"> arr
console.log(d) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> undefined</span></pre>
</div>
<p>2、对象解构和数组解构基本类似,只不过对象解构的取值方式是根据对象的属性名来取值</p>
<p>例如:</p>
<div class="cnblogs_code">
<pre>const obj = {name:'100',age:'30',size:'M'<span style="color: rgba(0, 0, 0, 1)">}
const { name } </span>=<span style="color: rgba(0, 0, 0, 1)"> obj
console.log(name) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 100</span></pre>
</div>
<p>顺便说一下,对象里面的属性名和其他自定义的变量名称如果重名的时候要怎么解决,一旦重名就会报错,看代码:</p>
<div class="cnblogs_code">
<pre>const obj = {name:'100',age:'30',size:'M'<span style="color: rgba(0, 0, 0, 1)">}
const name </span>= 'lucy'<span style="color: rgba(0, 0, 0, 1)">
const {name} </span>=<span style="color: rgba(0, 0, 0, 1)"> obj
console.log(name) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 会报错</span></pre>
</div>
<p>// 要么重新命名,要么可以按照下面的写法来避免</p>
<div class="cnblogs_code">
<pre>const obj = {name:'100',age:'30',size:'M'<span style="color: rgba(0, 0, 0, 1)">}
const name </span>= 'lucy'<span style="color: rgba(0, 0, 0, 1)">
const {name:nameObj} </span>= obj <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对象属性名称的重新指定</span>
console.log(nameObj) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 100</span></pre>
</div>
<p>思考题:</p>
<p>请问下面的代码是什么意思?控制台输出什么?</p>
<div class="cnblogs_code">
<pre> <script>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useBook() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ["ES6高级编程", (bookname) => console.log(bookname + "!"<span style="color: rgba(0, 0, 0, 1)">)];
}
const </span>=<span style="color: rgba(0, 0, 0, 1)"> useBook();
showBook(name);
</span></script></pre>
</div>
<p>答案:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_69e06c08-d241-4b17-b812-d1e3fb44fe86" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_69e06c08-d241-4b17-b812-d1e3fb44fe86" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_69e06c08-d241-4b17-b812-d1e3fb44fe86" class="cnblogs_code_hide">
<pre>ES6高级编程!</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<h2>2.2、Hooks基础</h2>
<p><em>Hook</em>是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state,一般搭配函数式组件使用。</p>
<p>在React 16.8之前,函数式组件只能作为无状态组件(只读组件),即不能进行状态管理。</p>
<p>函数式创建组件通常是无状态组件,这种方式没有办法在内部对状态统一管理,如果我们非要添加状态管理呢,那就只能借助redux啦~或者我们自己利用观察者模式实现一个发布订阅。</p>
<p>那么如果我们非要这么做,React版本在16.8.X以后增添了一个新特性就是hooks。</p>
<p>hooks涉及API有useState、 useEffect、 useCallback、 useRef、 useMemo、 React.memo、 useReducer等,具体可以参考官方文档。</p>
<h3>2.2.1、useState() hooks状态钩子</h3>
<p>搭配函数式组件,主要是可以进行组件的状态管理,好处是不像传统state需要注意this指向(函数式组件中没有this)。</p>
<p>一般用法 const [ a , setA ] = useState(初始值)</p>
<p>a表示组件需要声明的变量a,setA允许你在组件其它的位置对a的数据进行改变setA(2),即a的值将为2</p>
<p>一个组件中可以声明多个useState()</p>
<p>Counter3.jsx</p>
<div class="cnblogs_code">
<pre>import React,{useState} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter3(props){
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>
<h2>{props.name}</h2>
<p>
<button onClick={()=>setCount(count+1)}>{count}</button>
</p>
</div>);
}</pre>
</div>
<p>调用</p>
<div class="cnblogs_code">
<pre>const vnode =<span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<Counter3 name="函数式组件使用状态的计数器" />
</div>
);</pre>
</div>
<p>结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230323162636022-89081520.png" alt="" width="372" height="142" loading="lazy"></p>
<h3>2.2.2、useEffect 副作用钩子</h3>
<div>uesEffect()可以与class组件中的三个周期函数作用相同,可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。</div>
<div class="cnblogs_code">
<pre>import React,{useState,useEffect} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter3(props){
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(()</span>=><span style="color: rgba(0, 0, 0, 1)">{
console.log(`当前count的值为${count}`);
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>
<h2>{props.name}</h2>
<p>
<button onClick={()=>setCount(count+1)}>{count}</button>
</p>
</div>);
}</pre>
</div>
<p>运行时的初始状态:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324115009002-321280839.png" alt="" width="469" height="416" loading="lazy"></p>
<p>解释:</p>
<p>点击时的效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324115113872-480378832.png" alt="" width="624" height="549" loading="lazy"></p>
<p>解释:</p>
<p>卸载时执行,在userEffect回调函数中返回一个函数将在组件卸载与更新阶段执行:</p>
<p>Counter3.jsx</p>
<div class="cnblogs_code">
<pre>import React,{useState,useEffect} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter3(props){
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(()</span>=><span style="color: rgba(0, 0, 0, 1)">{
console.log(`当前count的值为${count}`);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"组件被卸载了"<span style="color: rgba(0, 0, 0, 1)">);
}
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>
<h2>{props.name}</h2>
<p>
<button onClick={()=>setCount(count+1)}>{count}</button>
</p>
</div>);
}</pre>
</div>
<p>Box.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import Counter3 from </span>"./Counter3"<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Box extends Component {
state </span>= { isShow: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> };
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>父组件容器</h2>
{<span style="color: rgba(0, 0, 255, 1)">this</span>.state.isShow ? <Counter3 /> : <span>子组件不显示</span><span style="color: rgba(0, 0, 0, 1)">}
</span><p>
<<span style="color: rgba(0, 0, 0, 1)">button
type</span>="button"<span style="color: rgba(0, 0, 0, 1)">
onClick</span>={() => <span style="color: rgba(0, 0, 255, 1)">this</span>.setState({ isShow: !<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.state.isShow })}
</span>><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 0, 255, 1)">this</span>.isShow ? "隐藏" : "显示"<span style="color: rgba(0, 0, 0, 1)">}
</span></button>
</p>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
}</span></pre>
</div>
<p>初始效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324141414447-804796691.png" alt="" width="345" height="308" loading="lazy"></p>
<p>点击时的效果</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324141555766-303525250.png" alt="" width="318" height="459" loading="lazy"></p>
<p>如果想仅在卸载时执行则可以指定第二个参数,声明要监视的成员,如:</p>
<div class="cnblogs_code">
<pre> useEffect(()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(`当前count的值为${count}`);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"组件被卸载了"<span style="color: rgba(0, 0, 0, 1)">);
}
}<span style="color: rgba(255, 0, 0, 1)">,[]);</span></span></pre>
</div>
<p>这里使用的是一个空数组表明没有任何成员的变化会引起useEffect执行,再看看卸载时的效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324141924197-402031119.png" alt="" width="416" height="400" loading="lazy"></p>
<p>点击时count的更新并没有触发useEffect执行,卸载将执行</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324142034631-793092148.png" alt="" width="261" height="312" loading="lazy"></p>
<p>如果挂载、更新、卸载都需要处理,则可以这样:</p>
<div class="cnblogs_code">
<pre>import React,{useState,useEffect} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter3(props){
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
useEffect(()</span>=><span style="color: rgba(0, 0, 0, 1)">{
console.log(`组件挂载或更新了${count}`);
},);
useEffect(()</span>=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"组件被卸载了"<span style="color: rgba(0, 0, 0, 1)">);
}
},[]);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>
<h2>{props.name}</h2>
<p>
<button onClick={()=>setCount(count+1)}>{count}</button>
</p>
</div>);
}</pre>
</div>
<p>效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324143601283-1317752102.png" alt="" width="305" height="398" loading="lazy"></p>
<p>我们使用 useEffect 重写了上面的例子,<strong>useEffect 第一个参数接收一个函数,可以用来做一些副作用比如异步请求,修改外部参数等行为,而第二个参数称之为dependencies,是一个数组,如果数组中的值变化才会触发 执行useEffect 第一个参数中的函数。返回值(如果有)则在组件销毁或者调用函数前调用。 </strong></p>
<ul>
<li>1.比如第一个 useEffect 中,理解起来就是一旦 count 值发生改变,则修改 documen.title 值;</li>
<li>2.而第二个 useEffect 中传递了一个空数组[],这种情况下只有在组件初始化或销毁的时候才会触发,用来代替 componentDidMount 和 componentWillUnmount,慎用;</li>
<li>3.还有另外一个情况,就是不传递第二个参数,也就是useEffect只接收了第一个函数参数,代表不监听任何参数变化。每次渲染DOM之后,都会执行useEffect中的函数。</li>
</ul>
<p>基于这个强大 Hooks,我们可以模拟封装出其他生命周期函数,比如 componentDidUpdate 代码十分简单</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useUpdate(fn) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useRef 创建一个引用</span>
const mounting = useRef(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (mounting.current) {
mounting.current </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
fn();
}
});
}</span></pre>
</div>
<p><span style="background-color: rgba(255, 255, 255, 1); font-size: 14px">现在我们有了 useState 管理状态,useEffect 处理副作用,异步逻辑,学会这两招足以应对大部分类组件的使用场景。 </span></p>
<p> </p>
<div class="cnblogs_code">
<pre>import React,{useState,useEffect} from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter8(){
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
let </span>=useState(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">副作用钩子</span>
useEffect(()=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"组件被卸载了!componentWillUnmount "<span style="color: rgba(0, 0, 0, 1)">);
};
},[]);
useEffect(()</span>=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"组件挂载成功了!componentDidMount"<span style="color: rgba(0, 0, 0, 1)">);
},[]);
useEffect(()</span>=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">isInit){
console.log(</span>"组件更新成功了!componentDidUpdate"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
setIsInit(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
}
},);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>
<h2>计数器 count={count} n={n}</h2>
<button onClick={()=>{setCount(count+1)}} >count++</button>
<button onClick={()=>{setN(n+1)}} >n++</button>
</div>)
}</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328092824644-1825673196.png" alt="" width="475" height="413" loading="lazy"></p>
<h3>2.2.3、useContext()获取上下文</h3>
<p>获取context 对象 用法:const value = useContext(MyContext);</p>
<p> 上面介绍了 useState、useEffect 这两个最基本的 API,接下来介绍的 useContext 是 React 帮你封装好的,用来处理多层级传递数据的方式,在以前组件树种,跨层级祖先组件想要给孙子组件传递数据的时候,除了一层层 props 往下透传之外,我们还可以使用 React Context API 来帮我们做这件事,举个简单的例子:<br>App.js
</p>
<div class="cnblogs_code">
<pre>import React from "react"<span style="color: rgba(0, 0, 0, 1)">;
const { Provider, Consumer } </span>= React.createContext(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Bar() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Consumer>{(color) => <h2>{color}</h2>}</Consumer><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Foo() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Bar />;
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><Provider value={"blue"}>
<Foo />
</Provider>
<span style="color: rgba(0, 0, 0, 1)">);
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> App;</pre>
</div>
<p>通过 React createContext 的语法,在 APP 组件中可以跨过 Foo 组件给 Bar 传递数据。而在 React Hooks 中,我们可以使用 useContext 进行改造。</p>
<div class="cnblogs_code">
<pre>import React, { useContext } from "react"<span style="color: rgba(0, 0, 0, 1)">;
const colorContext </span>= React.createContext("red"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Bar() {
const color </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(colorContext);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <h2>{color}</h2>;
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Foo() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Bar />;
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><colorContext.Provider value="blue">
<Foo />
</colorContext.Provider>
<span style="color: rgba(0, 0, 0, 1)">);
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> App;</pre>
</div>
<p>传递给 useContext 的是 context 而不是 consumer,返回值即是想要透传的数据了。用法很简单,使用 useContext 可以解决 Consumer 多状态嵌套的问题。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> HeaderBar() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><CurrentUser.Consumer><span style="color: rgba(0, 0, 0, 1)">
{user </span>=>
<Notifications.Consumer><span style="color: rgba(0, 0, 0, 1)">
{notifications </span>=>
<header><span style="color: rgba(0, 0, 0, 1)">
Welcome back, {user.name}</span>!<span style="color: rgba(0, 0, 0, 1)">
You have {notifications.length} notifications.
</span></header>
<span style="color: rgba(0, 0, 0, 1)"> }
}
</span></CurrentUser.Consumer>
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>而使用 useContext 则变得十分简洁,可读性更强且不会增加组件树深度。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> HeaderBar() {
const user </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(CurrentUser);
const notifications </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(Notifications);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><header><span style="color: rgba(0, 0, 0, 1)">
Welcome back, {user.name}</span>!<span style="color: rgba(0, 0, 0, 1)">
You have {notifications.length} notifications.
</span></header>
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>示例:</p>
<div class="cnblogs_code">
<pre>import React, { useState, createContext, useContext } from "react"<span style="color: rgba(0, 0, 0, 1)">;
const CountContext </span>= createContext(0<span style="color: rgba(0, 0, 0, 1)">);
const Example </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState<number>(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<p>父组件点击数量:{count}</p>
<button onClick={() => setCount(count + 1)}>{"点击+1"}</button>
<CountContext.Provider value={count}>
<Counter />
</CountContext.Provider>
</div>
<span style="color: rgba(0, 0, 0, 1)">);
};
const Counter </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const count </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(CountContext);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <p>子组件获得的点击数量:{count}</p>;
<span style="color: rgba(0, 0, 0, 1)">};
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Example;</pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324152859761-1512090628.png" alt="" width="464" height="187" loading="lazy"></p>
<h3>2.2.5、useReducer () 复杂状态管理</h3>
<p>useReducer 这个 Hooks 在使用上几乎跟 Redux/React-Redux 一模一样,唯一缺少的就是无法使用 redux 提供的中间件。我们将上述的计时器组件改写为 useReducer,</p>
<p>作用:复杂状态管理,跟redux本质上是一样的</p>
<div>
<p>函数式组件如果涉及到状态管理,我们需要借助redux,那么hooks需要吗,答案也是一样的,简单的状态管理我们可以通过useState来进行管理,如果比较复杂的状态管理呢,react hook给我们提供了方法 useReducer</p>
<div>
<div>
<h3>使用useReducer分四步</h3>
<ul>
<li>创建初始值<code>initialState</code></li>
<li><code>const reducer = (state, action) => {switch(action.type) {case:}}传入旧的数据<code>state</code>和创建所有操作<code>action</code> </code></li>
<li><code>const = useReducer(reducer,initialState)传给useReducer,得到读和写API,必须写在函数里面 </code></li>
<li><code>dispatch({type:'add '})</code>调用写的类型</li>
</ul>
<blockquote>
<p>总的来说,useReducer是useState的复杂版,所有useState的规则,useReducer都适用</p>
</blockquote>
</div>
<code>useReducer(dispatch,state,init)</code></div>
<div>实现一个计算器,原始版本:</div>
<div>
<div class="cnblogs_code">
<pre>import React, { useState } from "react"<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter9() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>计数器 count={count}</h2>
<<span style="color: rgba(0, 0, 0, 1)">button
onClick</span>={() =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(count </span>+ 5<span style="color: rgba(0, 0, 0, 1)">);
}}
</span>>
+5
</button>
<<span style="color: rgba(0, 0, 0, 1)">button
onClick</span>={() =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(count </span>- 3<span style="color: rgba(0, 0, 0, 1)">);
}}
</span>>
-3
</button>
<<span style="color: rgba(0, 0, 0, 1)">button
onClick</span>={() =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(</span>0<span style="color: rgba(0, 0, 0, 1)">);
}}
</span>><span style="color: rgba(0, 0, 0, 1)">
重置
</span></button>
<<span style="color: rgba(0, 0, 0, 1)">button
onClick</span>={() =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(count </span>* 10<span style="color: rgba(0, 0, 0, 1)">);
}}
</span>>
*10
</button>
</div>
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328100202227-367523406.png" alt="" width="400" height="171" loading="lazy"></p>
</div>
<div>增加复杂状态管理的版本:</div>
<div>
<div class="cnblogs_code">
<pre>import React, { useReducer } from 'react'
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> reducer(state,action){
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)">(action.type){
</span><span style="color: rgba(0, 0, 255, 1)">case</span> 'increment'<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> {count:state.count+<span style="color: rgba(0, 0, 0, 1)">action.payload};
</span><span style="color: rgba(0, 0, 255, 1)">case</span> 'decrement'<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> {count:state.count-<span style="color: rgba(0, 0, 0, 1)">action.payload};
</span><span style="color: rgba(0, 0, 255, 1)">case</span> 'reset'<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {count:action.payload};
</span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error('未知的action'<span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Counter4(props){
const </span>=useReducer(reducer,{count:0<span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>计数器 - count={state.count}</h2>
<button onClick={()=>{dispatch({type:"increment",payload:5})}}>+5</button>
<button onClick={()=>{dispatch({type:"decrement",payload:3})}}>-3</button>
<button onClick={()=>{dispatch({type:"reset",payload:0})}}>重置</button>
<button onClick={()=>{dispatch({type:"multiplication",payload:10})}}>乘10</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Counter4;</pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324170829630-1065809448.png" alt="" width="298" height="153" loading="lazy"></p>
<p> </p>
<p>用法跟 Redux 基本上是一致的,用法也很简单,算是提供一个 mini 的 Redux 版本。</p>
<div>
<div>
<p>用法与 useState 类似,从 useReducer 中得到读接口 state,写接口 dispatch。最后操作时传参给 dispatch 写接口。操作灵活多变,比 useState 好处就是能聚集所有的操作和各种状态量。着重理解这几行代码,读懂。</p>
<p>useReducer 算是 useState 的复杂版</p>
<p>使用 useReducer 分以下步骤:</p>
<ul>
<li><code>创建初始值的状态initialState</code></li>
<li><code>创建所有对状态的操作reducer(state,action)</code></li>
<li><code>传给useReducer,得到读和写的接口</code></li>
<li><code>调用写({'type':'操作类型'})</code></li>
</ul>
<h3>2.2.6、useCallback()缓存回调函数</h3>
<p>https://zh-hans.reactjs.org/reference/react/useCallback </p>
<p>避免传入的回调每次都是新的函数实例化而导致依赖组件重新渲染;用法:const memoizedCallback = useCallback(()=>{doSomething(a,b);},,);把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新;</p>
<div class="cnblogs_code">
<pre>const memoizedCallback =<span style="color: rgba(0, 0, 0, 1)"> useCallback(
() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
doSomething(a, b);
},
,
);</span></pre>
</div>
<div>
<div> 1.返回一个 memoized 回调函数。<br>
2.把内联回调函数及依赖项数组作为参数传入 <code>useCallback</code>,它将返回该回调函数的
memoized
版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 <code>shouldComponentUpdate</code>)的子组件时,它将非常有用。
</div>
<div>
<code>useCallback(fn, deps)</code> 相当于 <code>useMemo(() => fn, deps)。
英文文档
</code></div>
<div>
<p>作用:提升性能,缓存事件,减少没必要的渲染</p>
<p>当我们使用类组件创建时,我们会怎么绑定事件呢</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
super(props);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>
<p>{`hello~${name}`}</p>
<button onClick={() => { console.log('click') }}>Click</button>
</div>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<div>
<p>这样写会导致什么结果呢,就是当渲染的时候react会认为每一次绑定的事件都是新的,从而从新进行计算</p>
<p>改进如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class App extends React.Component {
constructor(props) {
super(props);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick = <span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick.bind(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
handleClick() {
console.log(</span>'click'<span style="color: rgba(0, 0, 0, 1)">)
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>
<p>{`hello~${name}`}</p>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.handleClick}>Click</button>
</div>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<div>
<p>我们讲触发函数绑定在this上,来缓存这个方法</p>
<p><strong>hooks</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>
<button onClick={() => setCount(1)} ></button>
</div>
}</pre>
</div>
<div>
<p>同样的问题这么写也是存在的,改进如下</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
let handleSetCount </span>= useCallback(() =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(</span>1<span style="color: rgba(0, 0, 0, 1)">);
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>
<button onClick={handleSetCount} ></button>
</div>
}</pre>
</div>
<p>我们通过useCallback来缓存这个事件达到优化效果</p>
<h3>2.2.7、useMemo()</h3>
<p>缓存每次传入的props,避免依赖的组件每次都重新渲染;用法:const memoizedValue = useMemo (()=>computeExpensiveValue(a,b),);把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。</p>
<p> useCallback 的功能完全可以由 useMemo 所取代,如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。<br>(1)、useCallback(fn, inputs)
</p>
<p>(2)、useMemo(() => fn, inputs)</p>
<p>1与2等价。所以上面的示例可以使用useMemo修改。</p>
<p>
唯一的区别是:useCallback 不会执行第一个参数函数,而是将它返回给你,而
useMemo
会执行第一个函数并且将函数执行结果返回给你。所以在前面的例子中,可以返回
handleClick 来达到存储函数的目的。
</p>
<p>
所以 useCallback
常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而 useMemo
更适合经过函数计算得到一个确定的值,比如记忆组件。
</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Parent({ a, b }) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Only re-rendered if `a` changes:</span>
const child1 = useMemo(() => <Child1 a={a} />, );
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Only re-rendered if `b` changes:</span>
const child2 = useMemo(() => <Child2 b={b} />, );
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><><span style="color: rgba(0, 0, 0, 1)">
{child1}
{child2}
</span></>
<span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<p>当 a/b 改变时,child1/child2 才会重新渲染。从例子可以看出来,只有在第二个参数数组的值发生变化时,才会触发子组件的更新。</p>
<p>Child1.jsx</p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Child1(props){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>
<h2>子组件1 - {props.msg}</h2>
</div>
}</pre>
</div>
<p>Child2.jsx</p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Child2(props){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>
<h2>子组件2 - {props.msg}</h2>
</div>
}</pre>
</div>
<p>Parent.jsx</p>
<div class="cnblogs_code">
<pre>import Child1 from './Child1'<span style="color: rgba(0, 0, 0, 1)">;
import Child2 from </span>'./Child1'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Parent({a,b}){
const part1</span>=()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"part1渲染了"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Child1 msg={`a的值发生了变化,现在是:${a}`}/>
<span style="color: rgba(0, 0, 0, 1)">}
const part2</span>=()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"part2渲染了"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Child2 msg={`b的值发生了变化,现在是:${b}`}/>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><><span style="color: rgba(0, 0, 0, 1)">
{part1()}
{part2()}
</span></>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Parent;</pre>
</div>
<p>ParentBox.jsx</p>
<div class="cnblogs_code">
<pre>import React,{useState,useCallback} from 'react'<span style="color: rgba(0, 0, 0, 1)">
import Parent from </span>'./Parent'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> ParentBox(props){
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
let </span>=useState(0<span style="color: rgba(0, 0, 0, 1)">);
let handleChangeA</span>=useCallback(e=><span style="color: rgba(0, 0, 0, 1)">{
setA(e.target.value);
},[])
let handleChangeB</span>=useCallback(e=><span style="color: rgba(0, 0, 0, 1)">{
setB(e.target.value);
},[])
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>
<h1>ParentBox组件 - a={a} b={b}</h1>
a:<input type="text" onChange={handleChangeA} />
b:<input type="text" onChange={handleChangeB} />
<div>
<Parent a={a} b={b} />
</div>
</div>)
}</pre>
</div>
<p>运行效果:</p>
<p>初始状态</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230327155152330-808704024.png" alt="" width="615" height="477" loading="lazy"></p>
<p>反复修改a的值</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230327155300618-1362885615.png" alt="" width="612" height="708" loading="lazy"></p>
<p> </p>
<p>解释:。。。</p>
<p>改进后的Parent.jsx</p>
<div class="cnblogs_code">
<pre>import React,{useMemo} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
import Child1 from </span>'./Child1'<span style="color: rgba(0, 0, 0, 1)">;
import Child2 from </span>'./Child1'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Parent({a,b}){
const part1</span>=useMemo(()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"part1渲染了"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Child1 msg={`a的值发生了变化,现在是:${a}`}/>
<span style="color: rgba(0, 0, 0, 1)">},);
const part2</span>=useMemo(()=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"part2渲染了"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Child2 msg={`b的值发生了变化,现在是:${b}`}/>
<span style="color: rgba(0, 0, 0, 1)">},);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><><span style="color: rgba(0, 0, 0, 1)">
{part1}
{part2}
</span></>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Parent;</pre>
</div>
<p>反复修改a的值:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230327155459909-595486829.png" alt="" width="545" height="504" loading="lazy"></p>
<h3>2.2.8、useRef() 返回一个可变的 ref 对象</h3>
<p>获取组件的真实节点;用法:const refContainer = useRef(initialValue);</p>
<p>useRef 跟 createRef 类似,都可以用来生成对 DOM 对象的引用,看个简单的例子:</p>
<div class="cnblogs_code">
<pre>import React, { useState, useRef } from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
let </span>= useState("Nate"<span style="color: rgba(0, 0, 0, 1)">);
let nameRef </span>=<span style="color: rgba(0, 0, 0, 1)"> useRef();
const submitButton </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
setName(nameRef.current.value);
};
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div className="App">
<p>{name}</p>
<div>
<input ref={nameRef} type="text" />
<button type="button" onClick={submitButton}><span style="color: rgba(0, 0, 0, 1)">
Submit
</span></button>
</div>
</div>
<span style="color: rgba(0, 0, 0, 1)">);
}</span></pre>
</div>
<p>useRef 返回的值传递给组件或者 DOM 的 ref 属性,就可以通过 ref.current 值<strong>访问组件或真实的 DOM 节点,重点是组件也是可以访问到的,从而可以对 DOM 进行一些操作,比如监听事件等等。 </strong></p>
<h1>三、useReducer代替Redux实现状态管理</h1>
<div>
<h3 data-id="heading-6">五、使用 useReducer 代替 Redux</h3>
<p>使用 createContext/useContext 模拟 Redux 的全局数据状态管理的作用域,useReducer 来表示全局数据状态管理中的所有读写操作。</p>
<p>在创建的上下文对象中能够及时更新数据,就类似于一个局部的 Redux。在以下代码中,<code>案例</code>:我将模拟一个 state 有三个变量 n,m,p。一个组件更新,其他组件的值也会连带着更新。</p>
<p>state/npmCtx.js</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 导入 React 模块</span>
import React from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一个 counterContext 上下文对象</span>
const counterContext =<span style="color: rgba(0, 0, 0, 1)"> React.createContext();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自定义 hook,返回 counterContext 上下文对象</span>
export <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useCounterContext() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> React.useContext(counterContext);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将 counterContext 导出默认对象</span>
export <span style="color: rgba(0, 0, 255, 1)">default</span> counterContext;</pre>
</div>
<p>state/counterReducer.js</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化状态变量</span>
export const initState =<span style="color: rgba(0, 0, 0, 1)"> {
n: </span>0, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 变量 n 的值为 0</span>
p: 0, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 变量 p 的值为 0</span>
m: 0, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 变量 m 的值为 0</span>
<span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> reducer 函数</span>
export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> reducer(state, action) {
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (action.type) {
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "incN": <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> action 的 type 为 "incN" 时</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> { ...state, n: state.n + action.payload }; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回一个新的 state,其中变量 n 为原值加上 action.payload</span>
<span style="color: rgba(0, 0, 255, 1)">case</span> "incP": <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果 action 的 type 为 "incP"</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> { ...state, p: state.p + action.payload }; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回一个新的 state,其中变量 p 为原值加上 action.payload</span>
<span style="color: rgba(0, 0, 255, 1)">case</span> "incM": <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果 action 的 type 为 "incM"</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> { ...state, m: state.m + action.payload }; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回一个新的 state,其中变量 m 为原值加上 action.payload</span>
<span style="color: rgba(0, 0, 255, 1)">case</span> "reset": <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果 action 的 type 为 "reset"</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> initState; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回初始的 state</span>
<span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error("Unknown action"); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果 action 的 type 不被识别,则抛出错误</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p>NCom.jsx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 导入名为 useCounterContext 的来自文件 './state/npmCtx.js' 的上下文</span>
import {useCounterContext} from './state/npmCtx.js'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> NCom(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用 useCounterContext() 提供的状态和分发函数进行状态管理</span>
const {state,dispatch}=<span style="color: rgba(0, 0, 0, 1)">useCounterContext();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回组件 JSX </span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>组件 NCom</h2>
<h3>n={state.n}</h3>
<h3>p={state.p}</h3>
<h3>m={state.m}</h3>
<button onClick={()=><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当点击时触发 dispatch 函数,发送类型为 "incN" 带有增量为 10</span>
dispatch({type:"incN",payload:10<span style="color: rgba(0, 0, 0, 1)">})
}}</span>>n+10</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}</span></pre>
</div>
<p>PCom.jsx</p>
<div class="cnblogs_code">
<pre>import {useCounterContext} from './state/npmCtx.js'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> PCom(){
const {state,dispatch}</span>=<span style="color: rgba(0, 0, 0, 1)">useCounterContext();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>组件 PCom</h2>
<h3>n={state.n}</h3>
<h3>p={state.p}</h3>
<h3>m={state.m}</h3>
<button onClick={()=>{dispatch({type:"incP",payload:5})}}>p+5</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}</span></pre>
</div>
<p>MCom.jsx</p>
<div class="cnblogs_code">
<pre>import {useCounterContext} from './state/npmCtx.js'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> MCom(){
const {state,dispatch}</span>=<span style="color: rgba(0, 0, 0, 1)">useCounterContext();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<h2>组件 MCom</h2>
<h3>n={state.n}</h3>
<h3>p={state.p}</h3>
<h3>m={state.m}</h3>
<button onClick={()=>{dispatch({type:"incM",payload:-3})}}>p-3</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}</span></pre>
</div>
<div>CounterParent.jsx</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入React库</span>
import React from 'react'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入计数器上下文</span>
import counterContext from './state/npmCtx.js'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入计数器Reducer和初始化状态</span>
import reducer,{initState} from './state/counterReducer.js'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入三个组件</span>
import NCom from './NCom.jsx'<span style="color: rgba(0, 0, 0, 1)">;
import PCom from </span>'./PCom.jsx'<span style="color: rgba(0, 0, 0, 1)">;
import MCom from </span>'./MCom.jsx'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义计数器父组件</span>
export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> CounterParent(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用计数器Reducer和初始化状态,创建状态和dispatch函数</span>
const =<span style="color: rgba(0, 0, 0, 1)"> React.useReducer(reducer,initState);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 渲染计数器父组件</span>
<div>
<h1>计数器</h1>
<span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> 使用计数器上下文,提供状态和dispatch函数 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">}
</span><counterContext.Provider value={{state,dispatch}}><span style="color: rgba(0, 0, 0, 1)">
{</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> 渲染三个计数器子组件 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">}
</span><NCom/>
<PCom/>
<MCom/>
</counterContext.Provider>
</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}</span></pre>
</div>
<p>运行效果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328144428564-1462076136.png" alt="" width="390" height="829" loading="lazy"></p>
</div>
<h1>四、Redux</h1>
<h2>3.1、概要</h2>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328151835690-940052593.png" alt="" loading="lazy"></p>
<div>一个专门用于做状态管理的JS库,基本上与react配合使用</div>
<div>作用:集中式管理react应用中多个组件共享的状态</div>
<div> </div>
<div>使用场景:</div>
<div>某个组件的状态,需要让其他组件可以随时拿到(共享)</div>
<div>一个组件需要改变另一个组件的状态(通信)</div>
<div>总体原则:能不用就不用,如果不用比较吃力才考虑使用</div>
<div> </div>
<div>Redux 是 JavaScript 状态容器,提供可预测化的状态管理。</div>
<div>Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。 </div>
<div>官网:https://redux.js.org/ </div>
<div>github: https://github.com/reduxjs/redux </div>
<div>中文帮助:https://www.redux.org.cn/ </div>
<div>
<p> 1、redux是一个专门用于做状态管理的JS库,并非react插件库,但基本和react配合开发使用<br>2.其作用是集中式管理react应用中多个组件共享的状态,及负责管理状态
</p>
<p>
<br><strong>什么情况下需要使用redux?<br>1、某个组件的状态,需要让其他组件随时拿到共享<br>2、一个组件需要改变另一个组件的状态(或是通信)
</strong></p>
</div>
<h2>3.2、第一个Redux程序</h2>
<h3 id="安装">3.2.1、安装</h3>
<p>安装稳定版:</p>
<pre class="language-"><code>npm install --save redux
</code></pre>
<p>以上基于使用 npm 来做包管理工具的情况下。 </p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328153531308-341520557.png" alt="" width="535" height="154" loading="lazy"></p>
<p>否则你可以直接在 unpkg 上访问这些文件,下载下来,或者把让你的包管理工具指向它。 </p>
<p>一般情况下人们认为 Redux 就是一些 CommonJS 模块的集合。这些模块就是你在使用 Webpack、Browserify、或者 Node 环境时引入的。如果你想追求时髦并使用 Rollup,也是支持的。 </p>
<p>你也可以不使用模块打包工具。<code>redux</code> 的 npm 包里 <code>dist</code> 目录包含了预编译好的生产环境和开发环境下的 UMD 文件。可以直接使用,而且支持大部分流行的 JavaScript 包加载器和环境。比如,你可以直接在页面上的 <code><span class="token tag"><span class="token tag"><span class="token punctuation"><script<span class="token punctuation">></span> 标签 中引入 UMD 文件,也可以</span></span></span></code><code>让 <code>Bower</code> 来安装。UMD 文件可以让你使用 <code>window.Redux</code> 全局变量来访问 Redux。 </code></p>
<p>Redux 源文件由 ES2015 编写,但是会预编译到 CommonJS 和 UMD 规范的 ES5,所以它可以支持 任何现代浏览器。你不必非得使用 Babel 或模块打包器来使用 Redux。 </p>
<h4 id="附加包">附加包</h4>
<p>多数情况下,你还需要使用 React 绑定库和开发者工具。 </p>
<pre class="language-"><code>npm install --save react-redux
npm install --save-dev redux-devtools
</code></pre>
<p>需要提醒的是,和 Redux 不同,很多 Redux 生态下的包并不提供 UMD 文件,所以为了提升开发体验,我们建议使用像 Webpack 和 Browserify 这样的 CommonJS 模块打包器。 </p>
<h3>3.2.2、原理图</h3>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328152621373-418566190.png" alt="" width="912" height="519" loading="lazy"></p>
<p> </p>
<p> </p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230328152853488-344773379.png" alt="" width="971" height="335" loading="lazy"></p>
<p> </p>
<h3>3.3.3、三大原则</h3>
<p>Redux 可以用这三个基本原则来描述:</p>
<h4 id="单一数据源">单一数据源</h4>
<p>整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。 </p>
<p>这让同构应用开发变得非常容易。来自服务端的 state 可以在无需编写更多代码的情况下被序列化并注入到客户端中。由于是单一的 state tree ,调试也变得非常容易。在开发中,你可以把应用的 state 保存在本地,从而加快开发速度。此外,受益于单一的 state tree ,以前难以实现的如“撤销/重做”这类功能也变得轻而易举。</p>
<pre class="language-"><code class="lang-js">console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(store<span class="token punctuation">.<span class="token function">getState<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">)
<span class="token comment">/* 输出
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/
</span></span></span></span></span></span></span></span></span></code></pre>
<h4 id="state-是只读的">State 是只读的</h4>
<p>唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。 </p>
<p>这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化处理,且严格按照一个接一个的顺序执行,因此不用担心 race condition 的出现。 Action 就是普通对象而已,因此它们可以被日志打印、序列化、储存、后期调试或测试时回放出来。</p>
<pre class="language-"><code class="lang-js">store<span class="token punctuation">.<span class="token function">dispatch<span class="token punctuation">(<span class="token punctuation">{
<span class="token literal-property property">type<span class="token operator">: <span class="token string">'COMPLETE_TODO'<span class="token punctuation">,
<span class="token literal-property property">index<span class="token operator">: <span class="token number">1
<span class="token punctuation">}<span class="token punctuation">)
store<span class="token punctuation">.<span class="token function">dispatch<span class="token punctuation">(<span class="token punctuation">{
<span class="token literal-property property">type<span class="token operator">: <span class="token string">'SET_VISIBILITY_FILTER'<span class="token punctuation">,
<span class="token literal-property property">filter<span class="token operator">: <span class="token string">'SHOW_COMPLETED'
<span class="token punctuation">}<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h4 id="使用纯函数来执行修改">使用纯函数来执行修改</h4>
<p>为了描述 action 如何改变 state tree ,你需要编写 reducers。 </p>
<p>Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器。</p>
<pre class="language-"><code class="lang-js">
<span class="token keyword">function <span class="token function">visibilityFilter<span class="token punctuation">(state <span class="token operator">= <span class="token string">'SHOW_ALL'<span class="token punctuation">, action<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">switch <span class="token punctuation">(action<span class="token punctuation">.type<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">case <span class="token string">'SET_VISIBILITY_FILTER'<span class="token operator">:
<span class="token keyword">return action<span class="token punctuation">.filter
<span class="token keyword">default<span class="token operator">:
<span class="token keyword">return state
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token keyword">function <span class="token function">todos<span class="token punctuation">(<span class="token parameter">state <span class="token operator">= <span class="token punctuation">[<span class="token punctuation">]<span class="token punctuation">, action<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">switch <span class="token punctuation">(action<span class="token punctuation">.type<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">case <span class="token string">'ADD_TODO'<span class="token operator">:
<span class="token keyword">return <span class="token punctuation">[
<span class="token operator">...state<span class="token punctuation">,
<span class="token punctuation">{
<span class="token literal-property property">text<span class="token operator">: action<span class="token punctuation">.text<span class="token punctuation">,
<span class="token literal-property property">completed<span class="token operator">: <span class="token boolean">false
<span class="token punctuation">}
<span class="token punctuation">]
<span class="token keyword">case <span class="token string">'COMPLETE_TODO'<span class="token operator">:
<span class="token keyword">return state<span class="token punctuation">.<span class="token function">map<span class="token punctuation">(<span class="token punctuation">(<span class="token parameter">todo<span class="token punctuation">, index<span class="token punctuation">) <span class="token operator">=> <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(index <span class="token operator">=== action<span class="token punctuation">.index<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">return Object<span class="token punctuation">.<span class="token function">assign<span class="token punctuation">(<span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">, todo<span class="token punctuation">, <span class="token punctuation">{
<span class="token literal-property property">completed<span class="token operator">: <span class="token boolean">true
<span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">}
<span class="token keyword">return todo
<span class="token punctuation">}<span class="token punctuation">)
<span class="token keyword">default<span class="token operator">:
<span class="token keyword">return state
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token keyword">import <span class="token punctuation">{ combineReducers<span class="token punctuation">, createStore <span class="token punctuation">} <span class="token keyword">from <span class="token string">'redux'
<span class="token keyword">let reducer <span class="token operator">= <span class="token function">combineReducers<span class="token punctuation">(<span class="token punctuation">{ visibilityFilter<span class="token punctuation">, todos <span class="token punctuation">}<span class="token punctuation">)
<span class="token keyword">let store <span class="token operator">= <span class="token function">createStore<span class="token punctuation">(reducer<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>就是这样,现在你应该明白 Redux 是怎么回事了。</p>
<h3>3.3.4、示例</h3>
<p> </p>
<p>应用程序的整个全局状态存储在单个存储中的对象树中。更改状态树的唯一方法是创建一个操作,一个描述发生了什么的对象,并将其分派到存储区。为了指定如何更新状态以响应某个操作,需要编写纯reducer函数,该函数根据旧状态和操作计算新状态。</p>
<p>store/counterReducer.js</p>
<div class="cnblogs_code">
<pre>export const initState =<span style="color: rgba(0, 0, 0, 1)"> {
count: </span>0<span style="color: rgba(0, 0, 0, 1)">,
};
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span> reducer(state =<span style="color: rgba(0, 0, 0, 1)"> initState, { type, data }) {
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (type) {
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "increment"<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> { count: state.count +<span style="color: rgba(0, 0, 0, 1)"> data };
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "decrement"<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> { count: state.count -<span style="color: rgba(0, 0, 0, 1)"> data };
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "reset"<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> initState;
</span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> initState;
}
}</span></pre>
</div>
<p>store/store.js</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">import { createStore } from "redux";</span>
import { legacy_createStore as createStore } from "redux"<span style="color: rgba(0, 0, 0, 1)">;
import reducer, { initState } from </span>"./counterReducer"<span style="color: rgba(0, 0, 0, 1)">;
const store </span>=<span style="color: rgba(0, 0, 0, 1)"> createStore(reducer, initState);
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> store;</pre>
</div>
<p>reduxCounter.jsx</p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
import store from </span>'./reduxState/store'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class ReduxCounter extends Component {
incrementHandle</span>=e=><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>"incrementHandle"<span style="color: rgba(0, 0, 0, 1)">);
store.dispatch({type: </span>'increment',data:5<span style="color: rgba(0, 0, 0, 1)">});
console.log(store.getState().count);
}
decrementHandle</span>=e=><span style="color: rgba(0, 0, 0, 1)">{
store.dispatch({type: </span>'decrement',data:3<span style="color: rgba(0, 0, 0, 1)">});
}
resetHandle</span>=e=><span style="color: rgba(0, 0, 0, 1)">{
store.dispatch({type: </span>'reset'<span style="color: rgba(0, 0, 0, 1)">});
}
componentDidMount() {
store.subscribe(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({})
})
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div style={{backgroundColor:"#00ccff"}}>
<h2>ReduxCounter</h2>
<h3>当前Count={store.getState().count}</h3>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.incrementHandle}>+5</button>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.decrementHandle}>-3</button>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.resetHandle}>重置</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>运行结果:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230329160740198-1816781401.png" alt="" width="379" height="649" loading="lazy"></p>
<p> </p>
<p> </p>
<p>测试多组件数据共享的情况</p>
<p>当前求和为奇数再加</p>
<p>异步加</p>
<p>redux中,当store数据更新后,界面数据并不会直接更新,需要手动更新</p>
<p>如果是函数式组件,手动更新方式如下</p>
<p> // redux中,当store数据更新后,界面数据并不会直接更新,需要手动更新<br>
// 初始化一个update数据使用useState(),主要是为了通过setUpdate()来更新组件<br>
// 模拟render()生命周期,实现组件重新加载,以更新界面的store数据
</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 大坑:redux中,当store数据更新后,界面数据并不会直接更新,需要手动更新</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化一个update数据使用useState(),主要是为了通过setUpdate()来更新组件</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 模拟render()生命周期,实现组件重新加载,以更新界面的store数据</span>
const =<span style="color: rgba(0, 0, 0, 1)"> useState({})
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> useEffect模拟componentDidMount()生命周期</span>
useEffect(() =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> store.subscribe()是redux提供的,监测store更新的函数</span>
store.subscribe(() =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当store数据更新后执行 setUpdate() ,组件重新加载,实现界面store数据更新</span>
<span style="color: rgba(0, 0, 0, 1)"> setUpdate({})
})
})</span></pre>
</div>
<p>类式组件,使用生命周期直接在store.subscribe()中调用setState()即可实现更新数据</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">componentDidMount() {
store.subscribe(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.setState({})
})
}</span></pre>
</div>
<p> 1、store.js:<br>(1)引入redux中的legacy_createStore(备注:createStore已废弃),创建一个store<br>(2)legacy_createStore调用时要传入一个为其服务的reducer<br>(3)暴露store对象<br>2、reducer.js:<br>(1)该文件用于创建一个为Count组件服务的reducer、reducer本质就是一个函数,接收preState,action、返回加工后的状态<br>(2)reducer有2个作用:初始化函数、加工状态<br>(3)reducer被第一次调用时,是store触发的,<br>传递的preState是undefined,<br>传递的action是类似如下格式数据:<br>{type:
“@@redux/INITr.y.u.u.5.o”}<br>3、action.js:该文件专门为所服务的组件生成action对象<br>4、constant.js:统一管理action中相关的常量,同时也避免写错常量字段<br>5、备注:可在index.js入口文件中监测store中状态的改变,一旦发生改变重新渲染
</p>
<h1>五、Mobx</h1>
<p>React的数据管理,除了redux之外,一个新的状态管理方案mobx</p>
<p>
传统React使用的数据管理库为Redux。Redux要解决的问题是统一数据流,数据流完全可控并可追踪。要实现该目标,便需要进行相关的约束。Redux由此引出了dispatch
action
reducer等概念,对state的概念进行强约束。然而对于一些项目来说,太过强,便失去了灵活性。Mobx便是来填补此空缺的。
</p>
<h1>六、ref基础知识</h1>
<p>
不管在Vue中还是React,如果我们想使用一个元素的DOM,不需要通过JS中操纵DOM的方法,它们提供了一个专属的API就是ref。
</p>
<p>
而Vue中的ref可能比较简单,这一篇主要讲一下如何在React中使用ref,以及使用ref的场景。
</p>
<h2>5.1、ref的挂载</h2>
<p>
在React中,ref可以挂载到html元素上,同时也可以挂载在React元素上,看下面的代码:
</p>
<p> </p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> import { findDOMNode } from 'react-dom'</span>
import Child from './Child'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Father extends Component {
componentDidMount(){
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.refs.refElement);
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.refs.child);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<input ref={ 'refElement' }></input>
<Child ref={ 'child' }/>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.fn}>123</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>控制台的打印为:</p>
<p><img src="https://img2023.cnblogs.com/blog/63651/202303/63651-20230324113105833-1565871250.png" alt="" width="535" height="70" loading="lazy"></p>
<p>可以看到,在React中,ref是可以挂载到HTML元素和React元素上的。</p>
<p>(1)挂载HTML元素,返回真实的DOM</p>
<p>(2)挂载React元素,返回render后的实例对象</p>
<p>同时React也提供了一个方法findDOMNode可以将React元素的ref返回变成真实的DOM元素。</p>
<div class="cnblogs_code">
<pre> import { findDOMNode } from 'react-dom'<span style="color: rgba(0, 0, 0, 1)">
console.log(findDOMNode(</span><span style="color: rgba(0, 0, 255, 1)">this</span>.refs.child));</pre>
</div>
<p>同时在上面的代码我们也可以看出来,ref的挂载是在componentDidMount等生命周期之前执行的。</p>
<h2>5.2、使用ref的三种方式</h2>
<h3>5.2.1、字符串的方式</h3>
<p> </p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Father extends Component {
componentDidMount(){
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.refs.refElement);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<input ref={ 'refElement' }></input>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.fn}>123</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>这种方式和Vue的ref比较相似,但是官方目前已经不推荐使用该方式,后续可能还会废弃。</p>
<h3>5.2.2、函数的方式</h3>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Father extends Component {
componentDidMount(){
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.refElement);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<input ref={ ref => <span style="color: rgba(0, 0, 255, 1)">this</span>.refElement = ref }></input>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.fn}>获取元素</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<h3>5.2.3、react.CreateRef的方式</h3>
<p> </p>
<div class="cnblogs_code">
<pre>import React, { Component } from 'react'<span style="color: rgba(0, 0, 0, 1)">
export </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> class Father extends Component {
refElement </span>=<span style="color: rgba(0, 0, 0, 1)"> React.createRef();
componentDidMount(){
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.refElement.current);
}
render() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<input ref={<span style="color: rgba(0, 0, 255, 1)">this</span>.refElement}></input>
<button onClick={<span style="color: rgba(0, 0, 255, 1)">this</span>.fn}>123</button>
</div>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}</span></pre>
</div>
<p>记住这里面通过refElement中的current,获取真实的DOM元素。</p>
<h1>七、打包一个项目</h1>
<div>npm run build ---生成一个build的文件夹</div>
<div>npm install -g serve —安装一个全局的serve服务器</div>
<div>serve -s build — 在服务器上运行打包的文件</div>
<div>运行之后谷歌的react开发者工具正常显示</div>
<h1>八、作业</h1>
<p>8.1、使用多种方法实现页面加载完成时让搜索文本框获取焦点,侧重练习ref的使用。</p>
<p>8.2、完成所有的上课示例。</p>
<h1>九、视频</h1>
<p> https://www.bilibili.com/video/BV1Vb411D7qa/?share_source=copy_web&vd_source=475a31f3c5d6353a782007cd4c638a8a </p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/best/p/17238735.html
頁:
[1]