React系列笔记学习
上篇笔记地址:【超全】React学习笔记 上:基础使用与脚手架
中篇笔记地址:【超全】React学习笔记 中:进阶语法与原理机制
React路由是构建单页面应用(SPA, Single Page Application)中的核心技术之一,它允许用户在不重新加载整个页面的情况下,实现页面的切换。通过React路由,开发者可以为用户提供丰富的页面导航体验,同时保持应用的性能和响应速度。
React路由通过定义一组路由规则,将URL与应用中的特定组件关联起来。用户通过点击链接或直接在浏览器中输入URL,可以快速导航到应用的不同部分。
了解React路由的执行过程有助于开发者理解路由是如何工作的,以及在遇到问题时如何调试和解决路由相关的问题。
除了通过链接导航,React路由还提供了编程式导航的能力,开发者可以通过代码来控制应用的导航行为,使应用能够根据用户的操作或其他条件动态地导航到不同的页面。
默认路由允许开发者为应用定义一个默认的页面,当用户访问的URL与任何已定义的路由规则都不匹配时,应用会自动导航到这个默认页面。
React路由提供了多种匹配模式,使开发者能够灵活地控制路由规则,匹配不同的URL模式,并在URL中捕获参数。
在接下来的学习中,我们将通过实例和代码示例,详细介绍React路由的使用方法和原理,让你能够熟练地利用React路由构建单页面应用。通过掌握React路由,你将能够为用户提供丰富、流畅的页面导航体验,同时保持应用的高性能和良好的可维护性。
现代的前端应用大多都是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而生。
React路由的基本使用非常简单和直观,下面是使用react-router-dom
库来创建基本路由的步骤:
通过 yarn 或 npm 安装 react-router-dom
库:
yarn add react-router-dom
# 或者
npm install react-router-dom
从 react-router-dom
中导入三个核心组件:BrowserRouter
(也可以重命名为Router
)、Route
和Link
。
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
Router
组件包裹整个应用:Router
组件应当包裹住你的整个应用,确保路由的上下文可用于应用的其他部分。
<Router>
<div className="App">
{/* ...省略页面内容 */}
</div>
</Router>
Link
组件作为导航菜单(路由入口):Link
组件提供了一个简单的方式来创建导航链接,它会渲染为HTML中的标签。
<Link to="/first">页面一</Link>
Route
组件配置路由规则和要展示的组件(路由出口):Route
组件定义了URL路径与组件之间的映射关系。你可以通过path
prop指定URL路径,通过component
prop指定要渲染的组件。
const First = () => <p>页面一的页面内容</p>;
<Router>
<div className="App">
<Link to="/first">页面一</Link>
<Route path="/first" component={First}></Route>
</div>
</Router>
除了BrowserRouter
,react-router-dom
库还提供了一个HashRouter
组件。HashRouter
和BrowserRouter
的工作方式略有不同,但都能提供基本的路由功能。
HashRouter
使用URL的hash部分(即#
符号后的部分)来保持UI和URL的同步。这种方式对于不能提供服务器端渲染支持的静态文件服务器非常有用,因为它不需要服务器配置即可提供深层链接。
以下是HashRouter
的基本使用方法:
import { HashRouter as Router, Route, Link } from 'react-router-dom';
const First = () => <p>页面一的页面内容</p>;
<Router>
<div className="App">
<Link to="/first">页面一</Link>
<Route path="/first" component={First}></Route>
</div>
</Router>
在上述代码中,我们从react-router-dom
导入了HashRouter
而不是BrowserRouter
,并将其重命名为Router
以保持代码的简洁和一致。其他的用法和BrowserRouter
基本相同,只是URL的格式会略有不同,例如/first
将变为#/first
。
React路由的执行过程可以理解为一个按步骤自动触发的序列,从用户点击Link
组件开始,到React路由渲染相应的Route
组件。下面是这个过程的详细解释:
Link
组件:当用户点击Link
组件时,它会修改浏览器地址栏中的URL。例如,如果Link
组件的to
prop是/first
,那么点击该Link
组件会使浏览器地址栏的URL变为/first
。
<Link to="/first">页面一</Link>
React路由库(react-router-dom
)会监听浏览器地址栏中的URL变化。这个监听过程是通过HTML5 History API或者哈希变化实现的,具体取决于你是使用BrowserRouter
还是HashRouter
。
Route
组件:一旦URL发生变化,React路由就会开始遍历所有的Route
组件,检查每个Route
组件的path
prop与当前的URL的pathname部分是否匹配。
<Route path="/first" component={First}></Route>
Route
组件内容:如果Route
组件的path
prop与当前的URL的pathname部分匹配,React路由就会渲染该Route
组件所指定的component
prop。在这个例子中,如果URL的pathname是/first
,First
组件就会被渲染到页面上。
const First = () => <p>页面一的页面内容</p>;
这个过程保证了当用户通过导航链接(由Link
组件提供)更改URL时,React路由能够正确地渲染与新URL对应的组件。通过这种方式,React路由提供了一个简单而强大的方式来构建单页应用程序(SPA),使开发者能够以一种组织良好、可维护的方式来管理应用的视图和导航逻辑。
在某些场景下,我们可能需要通过编程的方式来控制路由的跳转,例如,在用户登录成功后自动跳转到某个页面。React Router提供了一种编程式导航的方式,使得我们能够在JavaScript代码中控制路由的跳转。
场景说明:
假设我们有一个登录页面,当用户点击登录按钮并且登录成功后,我们想要通过代码将用户重定向到后台首页。
实现编程式导航:
React Router为我们提供了history
对象,它包含了与浏览器历史记录交互的一些方法。我们可以利用history
对象的push
和go
方法来实现编程式导航。
使用history.push
方法:
history.push
方法允许我们导航到一个新的位置,以下是如何在登录成功后使用history.push
方法跳转到后台首页的示例:
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Login extends Component {
handleLogin = () => {
// 假设登录验证逻辑已通过
this.props.history.push('/home');
};
render() {
return (
<div>
<button onClick={this.handleLogin}>登录</button>
</div>
);
}
}
export default withRouter(Login);
在上述代码中:
react-router-dom
导入了withRouter
高阶组件,它会将history
对象作为prop传递给Login
组件。handleLogin
方法中,我们调用了this.props.history.push('/home')
来导航到/home
路径。handleLogin
方法被调用,并将用户导航到后台首页。使用history.go
方法:
history.go
方法允许我们在浏览器历史记录中前进或后退。参数n
表示前进或后退的页面数量。例如,n
为-1表示后退到上一页:
goBack = () => {
this.props.history.go(-1);
};
在上述方法中,我们调用了this.props.history.go(-1)
来后退到上一页。类似地,我们可以使用n
为1来前进到下一页。
通过这种方式,React Router的history
对象为我们提供了强大而灵活的编程式导航功能,使我们能够在代码中精确控制路由的跳转。
在React Router中,有时我们可能想要为未匹配到的路径提供一个默认的页面,通常这个页面是一个“404 Not Found”页面,用来告诉用户他们访问了一个不存在的页面。为了实现这个功能,我们可以使用
组件和
组件来配置一个默认的路由。
使用
和
实现默认路由:
组件是用来包裹一组
组件的,它会从上到下匹配其中的
,一旦找到一个匹配的
,它就会渲染这个
并忽略其它的
。如果所有的
都没有匹配到,我们可以在
的最后放置一个没有path
属性的
作为默认路由。
以下是一个实现默认路由的示例:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const HomePage = () => <div>Home Page</div>;
const AboutPage = () => <div>About Page</div>;
const NotFoundPage = () => <div>404 Not Found</div>;
const App = () => {
return (
<Router>
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/about" component={AboutPage} />
{/* 默认路由 */}
<Route component={NotFoundPage} />
</Switch>
</Router>
);
};
export default App;
在上述代码中:
Router
, Route
和Switch
组件。
组件中,我们定义了几个具有特定path
属性的
组件。
组件的最后,我们添加了一个没有path
属性的
组件,并指定了NotFoundPage
组件作为它的component
属性值。这样,当用户访问一个未定义的路由时,NotFoundPage
组件会被渲染,从而展示一个“404 Not Found”页面。通过这种方式,我们可以为React应用配置一个简单而有效的默认路由,以处理未匹配到的路径。
React Router的匹配模式可以分为模糊匹配和精确匹配。模糊匹配是默认的匹配模式,它允许你在路径中定义一种模式,并且任何以该模式开头的路径都会被匹配。这可能会导致多个
组件被匹配和渲染。为了避免这种情况,你可能会想使用精确匹配模式。
在模糊匹配模式下,如果
组件的path
属性值是当前URL路径的前缀,那么该
组件就会被匹配。这种匹配模式允许你创建嵌套路由,但是也可能会导致不期望的路由匹配。
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const Home = () => <div>Home Page</div>;
const Login = () => <div>Login Page</div>;
const App = () => {
return (
<Router>
<div>
<Link to="/login">Login</Link>
{/* 模糊匹配 */}
<Route path="/" component={Home} />
<Route path="/login" component={Login} />
</div>
</Router>
);
};
export default App;
在上面的例子中,当你点击"Login"链接时,你会发现Home
组件和Login
组件都被渲染了。这是因为/
是/login
的前缀,所以
被匹配了。
这种模式可能不是你想要的,特别是当你有一个默认路由时。为了解决这个问题,你可以使用精确匹配模式。
通过为
组件添加exact
属性,你可以启用精确匹配模式。在这种模式下,只有当path和pathname完全匹配时,
组件才会被匹配。
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const Home = () => <div>Home Page</div>;
const Login = () => <div>Login Page</div>;
const App = () => {
return (
<Router>
<div>
<Link to="/login">Login</Link>
{/* 精确匹配 */}
<Route path="/" exact component={Home} />
<Route path="/login" component={Login} />
</div>
</Router>
);
};
export default App;
在上述代码中,我们通过添加exact
属性到
,确保只有当路径完全是/
时,Home
组件才会被渲染。现在,当你点击"Login"链接时,只有Login
组件会被渲染。
通过精确匹配模式,你可以避免不期望的路由匹配,特别是当你有多层路由结构时。所以,我们通常会推荐给默认路由添加exact属性。
路由表提供了一个集中的方式来定义应用中所有的路由路径和它们应该如何响应。在React中,使用路由表可以使应用结构更加清晰。
这些函数允许你为你的应用创建自定义路由。
history
API的路由器。在创建好路由表之后,我们可以使用RouterProvider
将路由表注入我们的APP内
import {createBrowserRouter, RouterProvider} from "react-router-dom"
const router = createBrowserRouter(
[
{
path: "/login",
element: 登录页
},
{
path: "/admin",
element: 管理页
}
])
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
)
路由系统中的多个路由之间需要进行路由跳转,并且在跳转的同时有可能需要传递参数进行通信。
声明式导航
声明式导航是指通过在模版中通过**组件描述出要跳转到哪里去**,比如后台管理系统的左侧菜单通常使用这种方式进行。
语法说明:通过给组件的to属性指定要跳转到路由path,组件会被渲染为浏览器支持的a链接,如果需要传参直接通过字符串拼接的方式拼接参数即可。
文章
编程式导航:命令化导航
编程式导航是指通过useNavigate
钩子得到导航方法,然后通过调用方法以命令式的形式进行路由跳转,比如想在登录请求完毕之后跳转就可以选择这种方式,更加灵活。
import {useNavigate} from "react-router-dom"
const App() =>
{
const navigate = useNavigate();
return (
我是登录页
)
}
语法说明:通过调用navigate方法传入地址path实现跳转.
searchParams
传参我们在navigate
函数里,在传入路由的路径时可以直接写入参数也可以直接传入参数,并使用searchParams
就可以进行使用了。
// 直接声明参数
const id = 1001
const name = 'user'
navigate(`/artical?id=${id}&name=${name}")
在searchParams
里使用传入的参数,使用get方法获取
const [params] = searchParams()
const id = params.get("id")
useParams
传参同样,在navigate函数内,我们还可以使用useParams
进行接收参数,接受到的参数是一个参数对象
const params = useParams()
const id = params.id
但是在使用useParams
传参之前,我们需要修改路由表的配置,我们需要用占位符来让路由器知道我们这个路由将要传入对应的参数。
const router = createBrowserRouter(
[
{
path: "/login",
element: 登录页
},
{
path: "/admin",
element: 管理页
},
{
path: "/article/:id",
element: 文章页
}
])
useLocation
传参在useLocation
传参过程中可以不需要路由表的配合,但是我们需要在中传入state参数。并在对应的路由组件内获取参数。
我是Link
在对应的路由组件内获取对应的参数
const Home() =>
{
const {state:{id, title, content}} = useLocation();
return (
{id}
{title}
{content}
)
}
实现步骤:
使用children属性配置路由嵌套关系
使用
组件配置二级路由渲染位置
首先,我们需要在路由表内声明当前路由下存在子路由,并声明路由组件
const router = createBrowserRouter(
[
{
path: "/",
element: 我是主页
children: [ // 使用children值来声明子路由
{
path: "home",
element: 我是主页的home
},
{
path: "about",
element:
}
]
}
{
path: "/login",
element: 登录页
},
{
path: "/admin",
element: 管理页
},
])
在路由组件之下,我们要使用
表示二级路由的渲染位置
const layout() => {
return (
我是layout
主页
关于页
{/*二级路由渲染位置*/}
)
}
嵌套路由的默认二级路由的配置
当访问的是一级路由时,默认的二级路由组件可以得到渲染,只需要在二级路由的位置去掉path,设置index属性为true。
比如,我们想进入/
路由之后,自动进入他的二级路由里的home
路由内,这个时候我们只需要把他的path去掉,设置index属性为true即可。
const router = createBrowserRouter(
[
{
path: "/",
element: 我是主页
children: [ // 使用children值来声明子路由
{
index: true,
element: 我是主页的home
},
{
path: "about",
element:
}
]
}
{
path: "/login",
element: 登录页
},
{
path: "/admin",
element: 管理页
},
])
404路由
场景∶当浏览器输入url的路径在整个路由配置中都找不到对应的path,为了用户体验,可以使用404兜底组件进行渲染
实现步骤:
准备一个NotFound组件;
在路由表数组的末尾,以*号作为路由path配置路由。
const router = createBrowserRouter(
[
{
path: "/",
element: 我是主页
children: [ // 使用children值来声明子路由
{
index: true,
element: 我是主页的home
},
{
path: "about",
element:
}
]
}
{
path: "/login",
element: 登录页
},
{
path: "/admin",
element: 管理页
},
{
path: "*",
element:
}
])
React路由是React应用中处理路由(即页面导航)的核心机制,它允许我们构建单页应用(SPA),在单个页面中展现多个视图,而无需重新加载整个页面。以下是对前面讨论内容的简单总结,以及对React路由基础的进一步阐述:
单页应用(SPA)与React路由:
核心组件:
组件只需要在应用的最外层使用一次。编程式导航:
props.history
对象,我们可以在JavaScript代码中直接控制路由的行为,例如导航到一个新页面。匹配模式:
pathname
以path
开头,就认为匹配成功。这种模式在某些情况下可能会导致意外的匹配结果。
组件添加exact
属性来启用精确匹配模式。在精确匹配模式下,只有当pathname
和path
完全相等时,才认为匹配成功。组件化的路由:
通过理解和应用以上几点,你可以构建出具有良好导航结构和用户体验的React应用。同时,React路由的组件化设计也使得路由的扩展和自定义变得非常简单和直观。
Redux 是一个用于JavaScript的状态容器,它提供可预测化的状态管理能力。通过Redux,你可以构建出具有一致性的应用,这些应用可以运行在不同的环境(如客户端、服务器、原生应用)中,并且非常容易进行测试。Redux不仅限于React,也可以与其他UI库(如Angular、Vue等)一起使用。Redux与React没有直接关联,如同Java与JavaScript并不是一个同门语言一般,但是Redux最多的使用场景就是与React共同合作管理全局组件的状态。
随着JavaScript单页面应用(SPA)的开发变得日益复杂,需要管理更多的state,包括但不限于服务器响应、缓存数据、本地生成尚未持久化到服务器的数据以及UI状态。如果不妥善管理,这些状态可能会相互影响,导致应用的行为难以预测。例如,一个模型(model)的变化可能会影响到另一个模型,而视图(view)的变化可能会进一步影响到模型,从而可能引发另一个视图的变化。Redux的出现,旨在通过引入严格的状态管理规则,解决这种复杂状态间的依赖和影响问题,使得状态的变化变得可预测和可控。
单一数据源:
State 是只读的:
type
属性来指示这个 action 的类型,以及其他一些数据。store.dispatch({ type: 'COMPLETE_TODO', index: 1 });
使用纯函数来执行修改:
通过这些核心原则,Redux提供了一个可靠且强大的状态管理解决方案,它可以帮助开发者构建复杂、可维护和可扩展的应用。
你已经很好地概述了 Redux 的组成部分和它们的功能。让我们为每个部分提供更多细节和清晰度。
State 是应用的状态树,它包含了应用的数据和UI状态。在React项目中,通常可以将 State 分为三类:
Actions是发送数据到store的载体。它们是描述“发生了什么”的对象,通常通过store.dispatch()
方法发送到store。
Action特点:
type
属性来表示要执行的动作,多数情况下,type
会被定义成字符串常量。type
字段外,action 对象的结构是自由的,可以根据需要添加其他属性。基本结构: 一个Action是一个简单的JavaScript对象,它至少包含一个type
属性,通常还包含一些数据。
{
type: 'ADD_TODO',
text: 'Learn Redux'
}
Action创建函数: 这些函数返回actions,它们是创建action的唯一方式,使得代码更清晰,也更容易测试。
function addTodo(text) {
return {
type: 'ADD_TODO',
text
};
}
序列图详解:
在这个序列图中:
Component
) 调用 store.dispatch(action)
方法来发送一个 Action
到 Store
。Store
调用 Reducer
,传递当前的 State
和 Action
作为参数。Reducer
返回一个新的 State
到 Store
。Store
更新 State
,然后重新渲染相关的组件。这个流程图简明地展示了在Redux中,如何通过store.dispatch()
方法发送一个Action,并通过Reducer来更新State,最终达到更新应用UI的目的。
Reducer 是一个函数,它响应发送过来的 actions,并返回新的 state。
Reducers指定了如何根据actions更新state。它们是纯函数,接收先前的state和一个action,并返回新的state。
基本结构:
function todoApp(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
// ...省略
default:
return state;
}
}
combineReducers
将它们组合在一起。序列图详解
在这个序列图中:
组件 (Component)
通过调用 store.dispatch(action)
方法发送一个 动作 (Action)
到 存储库 (Store)
,其中 Action 描述了“发生了什么”。存储库 (Store)
随后调用 Reducer
,传递当前的 状态 (State)
和 动作 (Action)
作为参数。Reducer 是一个纯函数,它根据接收到的 状态 (State)
和 动作 (Action)
计算并返回一个新的 状态 (State)
。 Reducer
返回新的 状态 (State)
到 存储库 (Store)
,随后 存储库 (Store)
更新 状态 (State)
。存储库 (Store)
更新了 状态 (State)
后,通知 组件 (Component)
重新渲染,以反映状态的变化。Store 是 Redux 的核心,它将 action 和 reducer 联系到一起,同时维护应用的 state。
getState()
方法获取 state。dispatch()
方法发送 action。subscribe()
方法注册监听器,监听 state 的变化。subscribe()
返回一个函数,用于注销监听器。createStore()
方法创建 store,传递 reducer 作为参数。import { createStore } from 'redux';
import todoApp from './reducers';
const store = createStore(todoApp);
通过理解 Redux 的这些组成部分以及它们的作用和交互方式,你将能够更好地理解和使用 Redux 来管理你的应用状态。
创建一个 React + Redux 项目涉及多个步骤。以下是从安装 Redux 到创建 Redux 文件夹的详细步骤。
首先,你需要创建一个新的React项目(如果你还没有一个的话)。
npx create-react-app my-redux-app
cd my-redux-app
你需要安装 Redux 和 React-Redux,React-Redux 是连接 React 和 Redux 的官方库。
npm install redux react-redux
在你的项目根目录下,创建一个名为 redux
的文件夹。这个文件夹将包含你所有的 Redux 相关代码(例如,reducers, actions, 和 middleware)。
mkdir src/redux
cd src/redux
创建Action文件夹
在 redux
文件夹中创建一个名为 actions
的文件夹,并在该文件夹中创建你的 action 文件。例如,你可以创建一个名为 index.js
的文件来存储你的 actions。
mkdir actions
touch actions/index.js
在actions文件里的代码文件里创建一个action函数,里面返回一个action对象。注意,action对象必须要有type属性
const sendAction () => {
// 返回一个action对象
return {
type : "send_action_type",
value: "发送了一个action"
}
}
把这个action创建的函数导出
module.exports = {
sendAction
}
Reducers: 在 redux
文件夹中创建一个名为 reducers
的文件夹,并在该文件夹中创建你的 reducer 文件。例如,你可以创建一个名为 index.js
的文件来组合和导出你的 reducers。
mkdir reducers
touch reducers/index.js
const rootReducer = (state, action) => {
// to do somethings.
}
const initState = {value : "default"}
const rootReducer = (state = initState, action) => {
// to do somethings.
}
在函数里面判断第二个参数action的type值是否是我们发送的
如果是的话,我们可以通过return返回新的state。
const initState = {value : "default"}
const rootReducer = (state = initState, action) => {
switch (action)
{
case send_type:
return Object.assign({/*new state object*/}, state, action);
default:
return state
}
}
const initState = {value : "default"}
const rootReducer = (state = initState, action) => {
switch (action)
{
case "send_type":
return Object.assign({/*new state object*/}, state, action);
default:
return state
}
}
module.exports = {
rootReducer
}
在 redux
文件夹中创建一个名为 store.js
的文件,用于创建和导出你的 Redux store。
touch store.js
在 store.js
文件中,你将导入 createStore
函数和你的 root reducer,然后使用它们来创建你的 store。
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(
rootReducer
);
export default store;
this.unSubbscribe = store.subscribe(() => {...} );
handleClick = () => {
store.dispath(sendAction())
}
React-Redux 是 Redux 的官方 React 绑定库,它允许你轻松地在 React 应用中集成和使用 Redux。通过 React-Redux,你可以让你的 React 组件连接到 Redux Store,共享和管理全局状态。React-Redux 提供了一些 API 和工具来简化在 React 中使用 Redux 的过程。
下面是 React-Redux 的一些主要特点和组成部分:
Provider
是一个 React 组件,它使得 Redux Store 可以在你的 React 应用中的任何地方被访问。你只需将 Provider
组件包裹在你的应用的根组件周围,并将你的 Redux Store 传递给它。import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux/store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
connect
函数是一个高阶函数,它返回一个新的连接到 Redux Store 的 React 组件。它接受两个参数 mapStateToProps
和 mapDispatchToProps
,分别用于将 Redux Store 中的状态和 dispatch 方法映射到你的 React 组件的 props。import { connect } from 'react-redux';
const mapStateToProps = state => ({
// map state to props
});
const mapDispatchToProps = dispatch => ({
// map dispatch to props
});
export default connect(mapStateToProps, mapDispatchToProps)(YourComponent);
useDispatch
和 useSelector
钩子来分别获取 dispatch 函数和选择 Redux Store 中的状态。import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
function YourComponent() {
const dispatch = useDispatch();
const state = useSelector(state => state);
// ...
}
React-Redux 不仅为你提供了一种在 React 中使用 Redux 的简单方法,还提供了性能优化和其他实用功能,以确保你的应用运行得更加顺畅。通过使用 React-Redux,你可以构建出结构清晰、可维护和可扩展的 React 应用。
React-Redux 是官方提供的 Redux 和 React 的绑定库。它使你能够将 Redux 的状态和 dispatch 方法映射到 React 组件的 props 中。使用 React-Redux,你可以轻松地在 React 应用中集成 Redux。
下面是 React-Redux 的基本使用步骤:
react-redux
和 redux
。npm install redux react-redux
编写React-redux代码
dispatch
函数发送到 store。// src/redux/actions.js
export const increment = () => ({
type: 'INCREMENT'
});
export const decrement = () => ({
type: 'DECREMENT'
});
// src/redux/reducers/index.js
import { combineReducers } from 'redux';
const initialState = {
count: 0
};
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1
};
case 'DECREMENT':
return {
...state,
count: state.count - 1
};
default:
return state;
}
}
const rootReducer = combineReducers({
counter: counterReducer
});
export default rootReducer;
// src/redux/store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;
connect
函数将 Redux 的 state 和 dispatch 方法映射到组件的 props 中。// src/components/Counter.js
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from '../redux/actions';
function Counter({ count, increment, decrement }) {
return (
{count}
);
}
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = {
increment,
decrement
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
Provider
包装应用: 最后,使用 react-redux
的 Provider
组件将你的应用包装在 Redux store 之中。Provider包裹下的所有react组件都会被传递同一个store,以此进行组件数据对象的使用。// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux/store';
import Counter from './components/Counter';
ReactDOM.render(
,
document.getElementById('root')
);
通过以上步骤,你应该已经成功地在你的 React 应用中集成了 Redux 和 React-Redux。现在,你可以在组件中通过 props 访问 Redux 的状态和 dispatch 方法,并能够轻松地管理和更新应用的状态。
React Toolkit (RTK) 是官方推荐的用于构建 Redux 应用程序的标准方法。它旨在简化 Redux 代码的编写和维护,并提供了许多有用的实用程序和中间件,以帮助开发人员更快更容易地构建复杂的应用程序。简单来说,React Toolkit是一套工具的集合,意在简化在React中编写redux逻辑的过程,通过一个函数传入对应的参数使得RTK帮你完成生成action和action type,也简化store创建的过程,以此实现简化书写方式。
安装: 你可以通过 npm 或 yarn 安装 React Toolkit。
npm install @reduxjs/toolkit react-redux # 需要安装好react-redux
# 或 yarn管理包工具
yarn add @reduxjs/toolkit react-redux # 需要安装好react-redux
使用React-Toolkit能够减少我们大量的逻辑编码的过程。
counterStore.js
import { createSlice } import "@reduxjs/toolkit"
const counterStore = createSlice({
name : "counter",
// 初始化状态数据
initialState: {
count: 0
}
// 修改数据的同步方法
reducers: {
inscrement(state){
state.count++;
},
decrement(state){
state.count--;
}
}
})
// 解构出创建的action对象函数 {actionCreater}
const {inscrement, decrement} = counterStore.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 导出创建action对象的函数和reducer函数
export {inscrement, decrement}
export default counterReducer
import { configureStore } from "@reduxjs/toolkit"
// 导入子模块reducer
import counterReducer from "./modules/counterStore"
// 创建根store组合的子模块
const store = configureStore({
reducer: {
counter: counterReducer
}
})
export default store
react-redux负责把Redux和React链接起来,内置Provider组件通过store参数把创建好的store实例注入到应用中,链接正式建立。
App.js
import { Provider } from "react-redux"
import store from "./store"
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<Provider store={store}>
<App />
<Provider>
)
在React组件中使用store中的数据,需要用到一个钩子函数: useSelector,它的作用是把store中的数据映射到组件中,使用样例如下:
const {count} = useSelector(state => state.counter)
这里的state.counter
是来自于src/store/index.js下的部分
const store = configureStore({
reducer: {
counter: counterReducer // state.counter来自于这里
}
})
pages/counter.js
import {useSelector} from "react-redux"
function App(){
const {count} = useSelector(state => state.counter)
return (
{count}
)
}
export default App
React组件中修改store中的数据需要借助另外一个hook函数– useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:
pages/counter.js
import {useSelector} from "react-redux"
// 导入之前创建的actionCreator
import {inscrement, decrement} from "./store/modules/counterStore"
function App(){
const {count} = useSelector(state => state.counter)
const dispath = useDispatch()
return (
{count}
)
}
export default App
总结:
useSelector
useDispatch
执行store模块中导出的actionCreater方法
在RTK里的异步状态处理的逻辑一般如下:
创建store的写法保持不变,配置好同步修改状态的方法
单独封装一个函数,在函数内部return一个新函数,在新函数中
- 封装异步请求获取数据
- 调用同步actionCreator传入异步数据生成一个action对象,并使用dispatch提交
组件中dispatch的写法保持不变。
完整编码:
store/modules/channelList.js
import {createSlice} from "@reduxjs/toolkit"
// 步骤1: 创建store的写法保持不变,配置好同步修改状态的方法
createSlice({
name: "channel",
initialState: {
channelList: []
},
reducers: {
setChannels(state, action)
{
state.channelList = action.payload
}
}
})
// 异步请求
const {setChannels} = channelStore.actions
// 步骤2: 单独封装一个函数,在函数内部return一个新函数
const fetchChannelList = () =>
{
return (dispatch) => { // 在新函数中
// 封装异步请求获取数据
const resultList = [
{id:1, name:"caixy"},
{id:2, name:"caixypromise"},
{id:3, name:"CAIXYPROMISE"}
]
// 调用同步actionCreator传入异步数据生成一个action对象,并使用dispatch提交
dispatch(setChannels(resultList))
}
}
export {fetchChannelList}
const reducer = channelStore.reducer
export default reducer;
src/App.js
import {useSelector, useDispatch} from "react-redux"
function App() {
const {channelList} = useSelector(state => state.channel)
const dispatch = useDispatch()
//使用useEffect触发异步执行
useEffect(() => {
dispatch(fetchChannelList())
}, [dispatch])// 利用dispatch不可变的规则,保证组件第一次渲染时执行。避免了每次组件渲染时都发送请求
return (
{channelList.map(
item => - {item.name}
)}
)
}
在这段代码中,useEffect
的使用是为了确保 dispatch(fetchChannelList())
只在组件第一次渲染时执行,而不是在每次渲染时执行。如果不使用 useEffect
,dispatch(fetchChannelList())
会在每次组件渲染时执行,可能会导致不必要的重复请求和渲染。
dispatch
是 Redux 提供的一个函数,它通常是不可变的。在 useEffect
的依赖项数组中包含 dispatch
,能确保 useEffect
中的代码只在组件第一次渲染时执行,而不是在每次渲染时执行。这样做可以减少不必要的请求和操作,提高应用的性能。
通过设置 useEffect
的依赖项数组为 [dispatch]
,你告诉 React 只有当 dispatch
函数发生变化时才重新执行 useEffect
中的代码。但由于 dispatch
函数通常不会变化,所以实际上 useEffect
中的代码只会在组件第一次渲染时执行。这种做法避免了每次组件渲染时都发送请求,节省了网络资源,提高了应用的性能。
r.net/gh/CaixyPromise/Blog_Image@main/img/202310241623879.png" alt=“image-20231024162250503” style=“zoom:80%;” />
counterStore.js
import { createSlice } import "@reduxjs/toolkit"
const counterStore = createSlice({
name : "counter",
// 初始化状态数据
initialState: {
count: 0
}
// 修改数据的同步方法
reducers: {
inscrement(state){
state.count++;
},
decrement(state){
state.count--;
}
}
})
// 解构出创建的action对象函数 {actionCreater}
const {inscrement, decrement} = counterStore.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 导出创建action对象的函数和reducer函数
export {inscrement, decrement}
export default counterReducer
import { configureStore } from "@reduxjs/toolkit"
// 导入子模块reducer
import counterReducer from "./modules/counterStore"
// 创建根store组合的子模块
const store = configureStore({
reducer: {
counter: counterReducer
}
})
export default store
react-redux负责把Redux和React链接起来,内置Provider组件通过store参数把创建好的store实例注入到应用中,链接正式建立。
App.js
import { Provider } from "react-redux"
import store from "./store"
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<Provider store={store}>
<App />
<Provider>
)
在React组件中使用store中的数据,需要用到一个钩子函数: useSelector,它的作用是把store中的数据映射到组件中,使用样例如下:
const {count} = useSelector(state => state.counter)
这里的state.counter
是来自于src/store/index.js下的部分
const store = configureStore({
reducer: {
counter: counterReducer // state.counter来自于这里
}
})
pages/counter.js
import {useSelector} from "react-redux"
function App(){
const {count} = useSelector(state => state.counter)
return (
{count}
)
}
export default App
React组件中修改store中的数据需要借助另外一个hook函数– useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:
pages/counter.js
import {useSelector} from "react-redux"
// 导入之前创建的actionCreator
import {inscrement, decrement} from "./store/modules/counterStore"
function App(){
const {count} = useSelector(state => state.counter)
const dispath = useDispatch()
return (
{count}
)
}
export default App
总结:
useSelector
useDispatch
执行store模块中导出的actionCreater方法
在RTK里的异步状态处理的逻辑一般如下:
创建store的写法保持不变,配置好同步修改状态的方法
单独封装一个函数,在函数内部return一个新函数,在新函数中
- 封装异步请求获取数据
- 调用同步actionCreator传入异步数据生成一个action对象,并使用dispatch提交
组件中dispatch的写法保持不变。
完整编码:
store/modules/channelList.js
import {createSlice} from "@reduxjs/toolkit"
// 步骤1: 创建store的写法保持不变,配置好同步修改状态的方法
createSlice({
name: "channel",
initialState: {
channelList: []
},
reducers: {
setChannels(state, action)
{
state.channelList = action.payload
}
}
})
// 异步请求
const {setChannels} = channelStore.actions
// 步骤2: 单独封装一个函数,在函数内部return一个新函数
const fetchChannelList = () =>
{
return (dispatch) => { // 在新函数中
// 封装异步请求获取数据
const resultList = [
{id:1, name:"caixy"},
{id:2, name:"caixypromise"},
{id:3, name:"CAIXYPROMISE"}
]
// 调用同步actionCreator传入异步数据生成一个action对象,并使用dispatch提交
dispatch(setChannels(resultList))
}
}
export {fetchChannelList}
const reducer = channelStore.reducer
export default reducer;
src/App.js
import {useSelector, useDispatch} from "react-redux"
function App() {
const {channelList} = useSelector(state => state.channel)
const dispatch = useDispatch()
//使用useEffect触发异步执行
useEffect(() => {
dispatch(fetchChannelList())
}, [dispatch])// 利用dispatch不可变的规则,保证组件第一次渲染时执行。避免了每次组件渲染时都发送请求
return (
{channelList.map(
item => - {item.name}
)}
)
}
在这段代码中,useEffect
的使用是为了确保 dispatch(fetchChannelList())
只在组件第一次渲染时执行,而不是在每次渲染时执行。如果不使用 useEffect
,dispatch(fetchChannelList())
会在每次组件渲染时执行,可能会导致不必要的重复请求和渲染。
dispatch
是 Redux 提供的一个函数,它通常是不可变的。在 useEffect
的依赖项数组中包含 dispatch
,能确保 useEffect
中的代码只在组件第一次渲染时执行,而不是在每次渲染时执行。这样做可以减少不必要的请求和操作,提高应用的性能。
通过设置 useEffect
的依赖项数组为 [dispatch]
,你告诉 React 只有当 dispatch
函数发生变化时才重新执行 useEffect
中的代码。但由于 dispatch
函数通常不会变化,所以实际上 useEffect
中的代码只会在组件第一次渲染时执行。这种做法避免了每次组件渲染时都发送请求,节省了网络资源,提高了应用的性能。