Redux 完整指南
各位程序员们大家好!今天我们将讨论使用 React 和 Redux 构建真正复杂的应用程序所需了解的一些概念。
在本文中,我们将详细介绍以下概念:
- 为什么我们需要 redux?
- 什么是 Redux?
- 创建 React-redux 应用程序的步骤
- 步骤 1:创建用户组件
- 第 2 步:创建商店
- 步骤3:创建 Reducer
- 步骤 4:与组件共享 Redux Store
- 步骤 5:使用 Redux Thunk 添加异步函数中间件
- 步骤 6:构建 Action Creator
- 步骤 7:将 Redux Store 连接到组件
开始吧!!🚀🚀
为什么选择 Redux?
在我们了解 Redux 的更多细节之前,首先尝试理解为什么我们真正需要它?
在一个具有许多组件的复杂应用程序中,如果您想在多个组件之间共享状态,那么您可以想到的一种方法就是使用props。
但是props并不能完全解决我们的问题,因为它只能让你使用自上而下的方式将数据从父组件发送到子组件,而不能反过来。这意味着子组件中发生的任何状态变化都不会影响父组件的状态。
另外, props未能解决的另一个问题是在没有父子层次结构的组件之间共享状态。
因此,为了克服上述所有问题并实现跨组件状态同步,Redux应运而生。通过这种方式,我们将所有状态存储在全局,所有其他组件都可以访问。
Redux是一个用于管理应用程序状态的开源 JavaScript 库。
什么是 Redux?
- Redux 主要用于状态管理。
- 它可以与所有 javascript 框架和库一起使用,例如 React、angular 等。
Redux 的主要元素包括:
- Store:如果你正在开发一个大型应用程序,状态会从 React 组件中分离出来,放到它自己的store中。store 是存储当前状态的全局组件,它是一个不可变的对象。
- 动作:商店中的状态不会直接改变,而是通过不同的动作来改变。
- Reducer:用于定义动作对应用程序状态的影响。
- 订阅:用于创建商店状态改变时调用的回调函数。
Redux 原则:
- 应用程序的全局状态作为对象存储在单个存储库中。
- 改变状态的唯一方法就是采取
dispatch
行动。 - 使用纯减速器函数进行更改。
让我们通过一个简单的例子来详细探讨每一个:
我们将遵循以下文件夹结构:
📦src
┣ 📂actions
┃ ┣ 📜types.js
┃ ┗ 📜users.js
┣ 📂components
┃ ┗ 📂Users
┃ ┃ ┣ 📜index.js
┃ ┃ ┗ 📜user.css
┣ 📂reducers
┃ ┣ 📜index.js
┃ ┗ 📜users.js
┣ 📂store
┃ ┗ 📜index.js
┣ 📜App.js
┗ 📜index.js
你可以在我的github repo中找到最终代码
现在我们将创建一个使用 REST API 获取用户数据并使用 Redux 显示它的应用程序。
最后,我们的应用程序将如下所示:
创建一个 React 应用程序并使用安装 redux npm install react-redux --save
。
步骤 1:创建用户组件
在src/components/Users/index.js
文件中:
import React, { useEffect, useState } from 'react';
import './user.css';
export default function Users() {
const [userDetails, setUserDetails] = useState([]);
const handleButtonClick = () => {
// make a call to Action Creator
}
return (
<div className="container">
<button className="btn" value="click me" onClick={handleButtonClick}>
Fetch Data
</button>
<table>
<tbody>
<tr>
<th>Id</th>
<th>Name</th>
<th>Phone</th>
<th>Email</th>
<th>Website</th>
</tr>
{
userDetails && userDetails.map((item, key) => {
return (
<tr>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.phone}</td>
<td>{item.email}</td>
<td>{item.website}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
)
}
在上面的代码中,每当用户单击按钮时,我们都会进行 API 调用以使用 REST API 获取数据并以表格格式显示数据。
但在进行 API 调用之前,让我们先设置我们的商店。
第 2 步:创建商店
我们将在文件中创建一个Redux 存储src/store/index.js
:
import { createStore } from "redux";
import rootReducer from "../reducers";
const preloadedState = {};
const store = createStore(
rootReducer,
preloadedState
);
export default store;
- Redux 核心库有一个createStore API,可以创建存储。
- 我们将传递下一步中创建的rootReducer作为参数。
- createStore还可以接受preloadedState值作为其第二个参数。您可以使用它在创建 store 时添加初始数据。
要记住的要点:
- 该商店基本上汇集了构成您的应用程序的
state
、actions
和。reducers
- Redux 应用程序中只能有一个存储。
- 每个 Redux 存储都有一个根 reducer 函数。
步骤3:创建 Reducer
- Reducers 基本上告诉我们如何根据执行的操作更新状态。
- 它必须是纯函数并且不应产生任何副作用。
- 它必须由不可变的对象组成。如果状态发生变化,旧对象不会被改变,而是被一个新的、改变的对象所取代。
让我们创建我们的reducer src/reducers/user.js
:
import { USER_DETAILS } from '../actions/types';
const initialState = {
userDetails: {}
}
export default function (state = initialState, action) {
console.log("Step 4: Inside User Reducer after action creator dispatches an action");
switch (action.type) {
case USER_DETAILS:
return {
...state,
userDetails: action.payload,
};
default:
return state;
}
}
它是一个函数,以当前状态和动作作为参数,返回一个新状态。
现在我们已经创建了一个减速器,但是随着我们的应用程序变得越来越复杂,我们可能需要引入更多的减速器。
因此在这种情况下,我们将创建主根Reducer,它将结合我们应用程序中使用的所有其他 Reducer。
在src/reducers/index.js
文件中:
import { combineReducers } from "redux";
import userReducer from "./users";
export default combineReducers({
userReducer: userReducer,
//other reducers
});
我们可以通过使用combineReducers函数组合两个或多个现有的reducer来为我们的应用程序创建实际的reducer 。
合并式Reducer 的工作方式是,每个action都会在合并后的 Reducer 的各个部分中得到处理。通常情况下,只有一个 Reducer 会对某个 action 感兴趣,但有时多个 Reducer 会根据同一个 action 更改各自的状态部分。
步骤 4:与组件共享 Redux Store
由于我们已经初步创建了我们的商店,下一步是使其可供我们应用程序中的所有组件使用。
在src/App.js
文件中:
import React from 'react';
import store from './store';
import { Provider } from 'react-redux';
import Users from './components/Users';
function App() {
return (
<Provider store={store}>
<Users/>
</Provider>
);
}
export default App;
通过这种方式,所有组件都可以访问商店。
步骤 5:使用 Redux Thunk 添加异步函数中间件
设置好商店后,现在我们需要进行 API 调用来获取数据,但在此之前,我们将向商店添加中间件,以便我们创建异步操作。
Redux Thunk
这个库就是所谓的redux-middleware,它必须随着store的初始化一起初始化。
因此,可以定义action-creators,以便它们返回以redux-store 的dispatch方法作为参数的函数。
因此,我们可以创建异步动作创建器,它首先等待某些操作完成,然后再分派真正的动作。
要将 redux-thunk 引入我们的应用程序,首先使用安装它npm install --save redux-thunk
。
现在在src/store/index.js
文件中:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers";
const preloadedState = {};
const middleware = [thunk];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducer,
preloadedState,
composeEnhancers(
applyMiddleware(...middleware)
)
);
export default store;
正如你所注意到的,我们在上面的代码中引入了许多新术语。让我们尝试逐一探索它们。
撰写
compose 是高阶函数的一个例子。它接受一组函数作为参数,并返回一个由所有这些函数组成的新函数。
- 当您想要将多个商店增强器传递给商店时使用它。
- 它从右到左组合单参数函数。最右边的函数可以接受多个参数,因为它为生成的复合函数提供了签名。例如:
compose(f, g, h)
与 doing 相同(...args) => f(g(h(...args)))
。
商店增强剂
- 它们是高阶函数,为 store 添加了一些额外的功能。Redux 默认提供的唯一 store 增强器是applyMiddleware。
应用中间件
- 创建一个 store 增强器,将中间件应用于Redux store 的dispatch方法。这对于各种任务都很方便,例如以简洁的方式表达异步操作或记录每个操作的有效负载。
- 因为中间件可能是异步的,所以这应该是组合链中的第一个存储增强器。
我们将在下一步中看到调度的用法。
步骤 6:构建 Action Creator
现在是时候创建一个使用 REST API 获取数据的动作创建器了。
动作创建者是一个创建动作的纯函数。
动作是普通的 JS 对象,它有一个type
字段,可以包含额外的数据。它会创建一个事件来描述应用程序中发生的事情。
我们将type
在单独的文件中声明所有字段src/actions/types.js
:
export const USER_DETAILS = 'USER_DETAILS';
构建 Action 创建器:-
在src/actions/user.js
文件中:
import axios from "axios";
import { USER_DETAILS } from './types';
export const getUserDetails = () => async (dispatch) => {
console.log("Step 2: Inside Action Creator to make an API call");
const res = await axios.get('https://jsonplaceholder.typicode.com/users');
console.log("Step 3: Dispatch an Action to update the state");
dispatch({
type: USER_DETAILS,
payload: res
})
}
在上面的代码片段中,我们进行了 API 调用,一旦我们得到响应,我们就分派动作,以便我们可以改变状态。
商店现在使用reducer来处理操作,这些操作通过其调度方法调度或“发送”到商店。
步骤 7:将 Redux Store 连接到组件
我们终于完成了商店的设置。只差一步,请继续关注🤓🤓。
在src/components/Users/index.js
文件中:
import React, { useEffect, useState } from 'react';
import { getUserDetails } from '../../actions/users';
import { connect } from "react-redux";
import './user.css';
function Users({ getUserDetails, userReducer }) {
const [userDetails, setUserDetails] = useState([]);
const handleButtonClick = () => {
//make a call to the Action creator
console.log("Step 1: Make a call to Action-creator from Users Component");
getUserDetails();
}
useEffect(() => {
// Update the UI as soon as we get our response through API call
console.log("Step 5: Inside UseEffect of User Component to update the UI")
setUserDetails(userReducer.userDetails.data);
}, [userReducer.userDetails])
return (
<div className="container">
.....
</div>
)
}
const mapStateToProps = (state) => ({
userReducer: state.userReducer
});
const mapDispatchToProps = {
getUserDetails
}
export default connect(mapStateToProps, mapDispatchToProps)(Users);
在上面的代码片段中,我们借助 与组件共享 redux 存储connect
。
高阶组件是一个接受“常规”组件作为参数并返回一个新的“常规”组件作为返回值的函数。
连接
-
react-redux 提供的connect方法是高阶组件的一个例子。
-
connect方法用于转换“常规” React 组件,以便 Redux 存储的状态可以“映射”到组件的 props 中。
-
它接受两个参数:
mapStateToProps
和mapDispatchToProps
。
mapStateToProps
它是一个函数,可用于定义基于 Redux 存储状态的连接组件的 props。
mapDispatchToProps
- 它是动作创建器函数的 JS 对象,作为道具传递给连接的组件。
- mapDispatchToProps中传递的函数必须是动作创建者,即返回 Redux 动作的函数。
正如您所注意到的,我们如何将动作创建者和redux 状态作为参数传递给用户组件。
点击按钮时,我们会调用 action-creator 进行 API 调用并更新 redux 状态。
我们useEffect
将监控 redux 状态的变化,并使用响应数据更新 UI。
终于,我们基于 Redux 的 React 应用准备好了!!!😎😎
我们可以使用Redux Devtools来测试和调试 Redux 状态如何变化。
你可以在我的github repo中找到最终代码
包起来!!
感谢您的时间!让我们一起学习,共同成长。
鏂囩珷鏉ユ簮锛�https://dev.to/anuradha9712/a-complete-guide-to-redux-hmj