React 之 Redux =》 理解+应用
文章目录
Redux基础介绍
一、概述
Redux是一个可预测的JavaScript应用程序状态管理容器。它主要用于JavaScript应用,特别是在React、Angular、Vue等框架中,但也可以与其他视图库一起使用。Redux遵循单向数据流的原则,使得应用的状态变化可预测,方便调试和维护。
二、元素组成
1. Action(动作)
- 定义:是一个包含
type
属性的普通JavaScript对象,用于描述发生了什么事情。type
通常是一个字符串常量,用于唯一标识这个动作。例如:
const ADD_TODO = 'ADD_TODO';
const addTodo = (text) => {
return {
type: ADD_TODO,
payload: text
};
}
- 作用:它是改变状态的唯一途径,就像一个“指令”,告诉Redux需要进行什么样的操作。
2. Reducer(纯函数)
- 定义:是一个纯函数,它接收两个参数,当前的
state
(应用的状态)和一个action
。根据action
的type
来决定如何更新state
,并返回新的state
。例如:
const initialState = {
todos: []
};
const todoReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, {text: action.payload, completed: false}]
};
default:
return state;
}
}
- 特点:
- 纯函数特性:给定相同的输入(相同的
state
和action
),总是返回相同的输出。没有副作用,如不会修改传入的参数,不会进行异步操作,也不会直接操作DOM。 - 不可变性:在更新
state
时,应该返回一个新的对象,而不是直接修改原有的state
。这有助于追踪状态的变化,并且在React等视图层中可以更高效地检测组件是否需要重新渲染。
- 纯函数特性:给定相同的输入(相同的
3. Store(仓库)
- 定义:是一个对象,它将
action
和reducer
联系在一起。通过createStore
函数创建(在Redux核心库中)。例如:
import { createStore } from 'redux';
const store = createStore(todoReducer);
- 功能:
- 保存状态:它存储着整个应用的状态树。可以通过
store.getState()
方法获取当前的状态。 - 分发动作:提供
store.dispatch(action)
方法,用于将action
发送给reducer
,从而触发状态的更新。 - 订阅更新:通过
store.subscribe(listener)
方法,可以订阅状态的变化。当状态更新时,会调用listener
函数,通常用于更新UI等操作。
- 保存状态:它存储着整个应用的状态树。可以通过
三、原理结构
Redux的工作流程是单向数据流:
- 动作触发:视图层(如React组件)通过用户交互或者其他事件(例如点击按钮)触发一个
action
。 - 动作分发:这个
action
被store.dispatch()
方法分发到reducer
。 - 状态更新:
reducer
根据传入的action
和当前state
,计算并返回一个新的state
。这个新的state
会替换掉旧的state
存储在store
中。 - 通知订阅者:
store
会通知所有订阅了状态变化的监听器(通过store.subscribe()
注册的函数)。这些监听器通常会更新视图层,使得UI能够反映最新的状态。
四、场景应用
1. 大型复杂的单页应用(SPA)
- 在大型SPA中,应用状态可能会非常复杂,涉及多个模块和组件之间的交互。例如,一个电商单页应用,有商品列表展示、购物车管理、用户登录状态等多个功能模块。
- 商品列表:当用户进行搜索操作时,会触发一个
SEARCH_PRODUCTS
类型的action
,reducer
会根据这个action
更新商品列表的状态。 - 购物车管理:添加商品到购物车会触发
ADD_TO_CART
类型的action
,从购物车移除商品会触发REMOVE_FROM_CART
等动作,这些动作通过reducer
来更新购物车状态。Redux可以很好地管理这些复杂的状态变化,确保每个模块的状态更新是可预测的。
2. 多用户协作的应用
- 考虑一个实时文档编辑应用,多个用户可以同时编辑一个文档。
- 当一个用户输入内容时,会触发
UPDATE_DOCUMENT
类型的action
。Redux可以协调各个用户的操作,通过服务器同步数据,使得每个用户看到的文档状态都是最新的。reducer
会根据这些操作更新文档的内容状态,并且通过store
的订阅机制,及时更新每个用户的视图。
3. 数据持久化和缓存管理
- 对于一些需要离线支持或者频繁请求相同数据的应用。例如,一个新闻阅读应用,它可以使用Redux来管理已缓存的新闻文章。
- 当应用首次加载新闻文章时,会触发
FETCH_ARTICLES
类型的action
,reducer
更新状态来存储获取到的文章列表。如果应用离线,它可以从Redux存储的状态中读取已缓存的文章,提供给用户阅读。同时,当用户手动刷新文章列表或者设置了定时更新时,又会触发新的FETCH_ARTICLES
动作来更新缓存。
五、实例
1. 简单的计数器应用
- 创建Action
// 定义action类型
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// 创建action创建函数
const increment = () => {
return {
type: INCREMENT
};
}
const decrement = () => {
return {
type: DECREMENT
};
}
- 创建Reducer
const initialState = {
count: 0
};
const 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;
}
}
- 创建Store并使用
import { createStore } from 'redux';
const store = createStore(counterReducer);
// 订阅状态变化并更新UI(这里简单打印)
const unsubscribe = store.subscribe(() => {
console.log(store.getState().count);
});
// 分发动作
store.dispatch(increment());
store.dispatch(increment());
store.dispatch(decrement());
// 取消订阅
unsubscribe();
2. 待办事项列表应用
- 创建Action
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const addTodo = (text) => {
return {
type: ADD_TODO,
payload: text
};
}
const toggleTodo = (index) => {
return {
type: TOGGLE_TODO,
payload: index
};
}
- 创建Reducer
const initialState = {
todos: []
};
const todoReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, {text: action.payload, completed: false}]
};
case TOGGLE_TODO:
const newTodos = [...state.todos];
newTodos[action.payload].completed =!newTodos[action.payload].completed;
return {
...state,
todos: newTodos
};
default:
return state;
}
}
- 创建Store并使用
const store = createStore(todoReducer);
// 订阅状态变化并更新UI(这里简单打印)
const unsubscribe = store.subscribe(() => {
console.log(store.getState().todos);
});
// 分发动作
store.dispatch(addTodo("Buy milk"));
store.dispatch(addTodo("Read a book"));
store.dispatch(toggleTodo(0));
unsubscribe();
Redux 之 React
在React中使用Redux
一、安装Redux相关库
- 首先需要安装
redux
和react-redux
库。- 如果使用npm,可以在项目目录下的终端中运行以下命令:
npm install redux react-redux
- 如果使用yarn,则运行:
yarn add redux react-redux
二、创建Redux Store和相关逻辑
- 定义Actions(动作)
- 在React项目中,通常在一个单独的文件(如
actions.js
)中定义动作。例如,对于一个简单的计数器应用,定义增加和减少计数的动作:
// actions.js export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; export const increment = () => { return { type: INCREMENT }; }; export const decrement = () => { return { type: DECREMENT }; };
- 在React项目中,通常在一个单独的文件(如
- 创建Reducers(纯函数)
- 同样在一个单独的文件(如
reducers.js
)中创建。以计数器应用为例,创建一个简单的reducer来处理计数状态的更新:
// reducers.js import { INCREMENT, DECREMENT } from './actions'; const initialState = { count: 0 }; const 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; } }; export default counterReducer;
- 同样在一个单独的文件(如
- 创建Store(仓库)
- 在项目的入口文件(如
index.js
)或者专门的store.js
文件中创建store。例如:
// store.js import { createStore } from 'redux'; import counterReducer from './reducers'; const store = createStore(counterReducer); export default store;
- 在项目的入口文件(如
三、在React组件中使用Redux
- 使用
Provider
组件包裹根组件- 在
index.js
文件中,将整个React应用包裹在Provider
组件内,使得应用中的所有组件都可以访问Redux store。Provider
是react - redux
库提供的组件。
import React from 'react'; import ReactDOM from 'react - dom'; import App from './App'; import { Provider } from 'react - redux'; import store from './store'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
- 在
- 在组件中连接Redux(使用
connect
函数)- 功能性组件(使用
useSelector
和useDispatch
)- 对于功能性组件,可以使用
react - redux
提供的useSelector
和useDispatch
钩子函数来获取状态和分发动作。例如,在一个计数器组件中:
import React from 'react'; import { useSelector, useDispatch } from 'react - redux'; import { increment, decrement } from './actions'; const Counter = () => { const count = useSelector((state) => state.count); const dispatch = useDispatch(); return ( <div> <p>Count: {count}</p> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> </div> ); }; export default Counter;
- 对于功能性组件,可以使用
- 类组件(使用
connect
函数)- 对于类组件,使用
connect
函数来连接Redux。例如,将上面的计数器组件改写为类组件:
import React, { Component } from 'react'; import { connect } from 'react - redux'; import { increment, decrement } from './actions'; class Counter extends Component { render() { const { count, increment, decrement } = this.props; return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); } } const mapStateToProps = (state) => ({ count: state.count }); const mapDispatchToProps = { increment, decrement }; export default connect(mapStateToProps, mapDispatchToProps)(Counter);
- 对于类组件,使用
mapStateToProps
函数- 这个函数用于将Redux store中的状态映射到组件的
props
中。它接收整个Redux状态树作为参数,返回一个对象,对象中的属性会被合并到组件的props
中。例如,在上面的类组件中,mapStateToProps
将state.count
映射为组件的count
属性。
- 这个函数用于将Redux store中的状态映射到组件的
mapDispatchToProps
函数(或对象)- 这个函数(或对象)用于将动作创建函数映射到组件的
props
中,使得组件可以通过props
来分发动作。在类组件的例子中,mapDispatchToProps
是一个对象,它将increment
和decrement
动作创建函数直接作为属性,这些属性会被添加到组件的props
中,从而可以在组件中通过this.props.increment
和this.props.decrement
来分发动作。在功能性组件中,useDispatch
钩子函数返回的dispatch
函数用于手动分发动作,更加灵活。
- 这个函数(或对象)用于将动作创建函数映射到组件的
- 功能性组件(使用
类组件connect具体应用
导出复用原理
connect
函数的作用机制- 在React - Redux中,
connect
函数是一个高阶函数,用于将React组件与Redux的store
连接起来。它接收两个参数:mapStateToProps
和mapDispatchToProps
(这两个参数也可以是函数或者对象,用于不同的映射方式)。 mapStateToProps
原理:这个函数用于将Reduxstore
中的状态映射到组件的props
中。它会在store
状态发生变化时被调用,每次调用都会重新计算返回的对象。返回对象中的属性会作为props
传递给组件。例如,如果mapStateToProps
返回{ count: state.count }
,那么组件就会通过props.count
来访问Redux中的count
状态。mapDispatchToProps
原理:这个函数(或对象)用于将Redux中的action
创建函数映射到组件的props
中。当它是一个函数时,接收dispatch
作为参数,返回一个对象,对象中的属性是action
创建函数,这些函数内部会调用dispatch
来分发action
。当它是一个对象时,对象中的属性直接就是action
创建函数,connect
函数会自动将它们绑定到dispatch
上,这样组件就可以通过props
来调用这些action
创建函数。
- 在React - Redux中,
- 组件复用原理
- 当一个通过
connect
函数连接了Redux的组件被导出后,它可以在多个地方被复用。因为mapStateToProps
和mapDispatchToProps
的映射关系是基于组件的使用场景和Redux的store
结构来定义的,所以只要Reduxstore
的结构和组件所需的状态及action
保持一致,组件就可以在不同的地方复用。例如,一个显示用户信息的组件,它通过mapStateToProps
获取用户状态,通过mapDispatchToProps
获取更新用户信息的action
函数,只要在其他地方也有相同的Reduxstore
结构和对用户信息的操作需求,这个组件就可以直接复用。
- 当一个通过
实例
1. 安装依赖
确保项目中安装了 redux
和 react-redux
:
npm install redux react-redux
2. 创建Redux相关文件
actions.js
定义购物车相关的 action
类型和 action
创建函数。
// actions.js
export const ADD_ITEM = 'ADD_ITEM';
export const REMOVE_ITEM = 'REMOVE_ITEM';
export const INCREMENT_QUANTITY = 'INCREMENT_QUANTITY';
export const DECREMENT_QUANTITY = 'DECREMENT_QUANTITY';
export const addItem = (product) => ({
type: ADD_ITEM,
payload: product
});
export const removeItem = (productId) => ({
type: REMOVE_ITEM,
payload: productId
});
export const incrementQuantity = (productId) => ({
type: INCREMENT_QUANTITY,
payload: productId
});
export const decrementQuantity = (productId) => ({
type: DECREMENT_QUANTITY,
payload: productId
});
reducers.js
创建购物车的 reducer
,处理各种 action
对购物车状态的更新。
// reducers.js
import { ADD_ITEM, REMOVE_ITEM, INCREMENT_QUANTITY, DECREMENT_QUANTITY } from './actions';
const initialState = {
cart: []
};
const cartReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_ITEM:
return {
...state,
cart: [...state.cart, {
...action.payload,
quantity: 1
}]
};
case REMOVE_ITEM:
return {
...state,
cart: state.cart.filter(item => item.id!== action.payload)
};
case INCREMENT_QUANTITY:
return {
...state,
cart: state.cart.map(item =>
item.id === action.payload? {
...item,
quantity: item.quantity + 1
} : item
)
};
case DECREMENT_QUANTITY:
return {
...state,
cart: state.cart.map(item =>
item.id === action.payload && item.quantity > 1? {
...item,
quantity: item.quantity - 1
} : item
)
};
default:
return state;
}
};
export default cartReducer;
store.js
创建Redux的 store
。
// store.js
import { createStore } from'redux';
import cartReducer from './reducers';
const store = createStore(cartReducer);
export default store;
3. 创建React类组件
Product.js
定义商品展示组件,该组件可以触发添加商品到购物车的操作。
// Product.js
import React, { Component } from'react';
import { connect } from'react - redux';
import { addItem } from './actions';
class Product extends Component {
render() {
const { product, addItem } = this.props;
return (
<div>
<h3>{product.name}</h3>
<p>{product.price}</p>
<button onClick={() => addItem(product)}>Add to Cart</button>
</div>
);
}
}
const mapDispatchToProps = {
addItem
};
export default connect(null, mapDispatchToProps)(Product);
CartItem.js
定义购物车中商品项的展示组件,该组件可以触发移除商品、增加和减少商品数量的操作。
// CartItem.js
import React, { Component } from'react';
import { connect } from'react - redux';
import { removeItem, incrementQuantity, decrementQuantity } from './actions';
class CartItem extends Component {
render() {
const { item, removeItem, incrementQuantity, decrementQuantity } = this.props;
return (
<div>
<p>{item.name}</p>
<p>Quantity: {item.quantity}</p>
<button onClick={() => incrementQuantity(item.id)}>Increment</button>
<button onClick={() => decrementQuantity(item.id)}>Decrement</button>
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
);
}
}
const mapDispatchToProps = {
removeItem,
incrementQuantity,
decrementQuantity
};
export default connect(null, mapDispatchToProps)(CartItem);
Cart.js
定义购物车展示组件,复用 CartItem
组件展示购物车中的所有商品。
// Cart.js
import React, { Component } from'react';
import { connect } from'react - redux';
import CartItem from './CartItem';
class Cart extends Component {
render() {
const { cart } = this.props;
return (
<div>
<h2>Shopping Cart</h2>
{cart.map(item => (
<CartItem key={item.id} item={item} />
))}
</div>
);
}
}
const mapStateToProps = (state) => ({
cart: state.cart
});
export default connect(mapStateToProps)(Cart);
App.js
在 App
组件中复用 Product
和 Cart
组件,展示商品列表和购物车。
// App.js
import React, { Component } from'react';
import { Provider } from'react - redux';
import store from './store';
import Product from './Product';
import Cart from './Cart';
const products = [
{ id: 1, name: 'Product 1', price: 10 },
{ id: 2, name: 'Product 2', price: 15 }
];
class App extends Component {
render() {
return (
<Provider store = {store}>
<div>
<h1>Redux Shopping Cart Example</h1>
<div>
<h2>Products</h2>
{products.map(product => (
<Product key={product.id} product={product} />
))}
</div>
<Cart />
</div>
</Provider>
);
}
}
export default App;
4. 总结
在上述代码中,Product
组件和 CartItem
组件都通过 connect
函数与Redux建立连接,能够触发Redux中的方法(action
)。Cart
组件复用了 CartItem
组件来展示购物车中的商品。整个应用通过 Provider
组件将Redux的 store
提供给所有子组件,实现了状态管理和组件间的交互。
redux 中间件应用及介绍
- Redux-Thunk
- 作用:
- 用于处理异步操作。它允许
action
创建函数返回一个函数,而不是一个普通的action
对象。这个返回的函数可以接收dispatch
和getState
作为参数,从而在函数内部执行异步操作(如网络请求),并根据异步操作的结果来分发action
。
- 用于处理异步操作。它允许
- 实例:
- 假设我们要从一个API获取用户数据并更新Redux状态。
- 首先,定义
action
类型和action
创建函数:// actions.js const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'; const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE'; const fetchUser = () => { return async (dispatch) => { try { const response = await fetch('https://example.com/api/user'); const userData = await response.json(); dispatch({ type: FETCH_USER_SUCCESS, payload: userData }); } catch (error) { dispatch({ type: FETCH_USER_FAILURE, payload: error.message }); } }; };
- 然后,创建
reducer
来处理action
:// reducers.js const initialState = { user: null, error: null }; const userReducer = (state = initialState, action) => { switch (action.type) { case FETCH_USER_SUCCESS: return { ...state, user: action.payload }; case FETCH_USER_FAILURE: return { ...state, error: action.payload }; default: return state; } }; export default userReducer;
- 最后,在创建
store
时应用redux - thunk
中间件:// store.js import { createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux - thunk'; import userReducer from './reducers'; const store = createStore( userReducer, applyMiddleware(thunkMiddleware) );
- 作用:
- Redux-Saga
- 作用:
- 也是用于处理异步操作,它基于
Generator
函数。Redux - Saga
将异步操作看作是可以被暂停和恢复的一系列步骤,通过Generator
函数的yield
关键字来暂停Saga
的执行,等待异步操作完成后再恢复执行。这样可以更方便地处理复杂的异步流程,如并发请求、轮询等。
- 也是用于处理异步操作,它基于
- 实例:
- 同样是获取用户数据的例子。
- 首先,定义
Saga
函数:// sagas.js import { call, put, takeEvery } from 'redux - saga/effects'; const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'; const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE'; function* fetchUserSaga() { try { const response = yield call(fetch, 'https://example.com/api/user'); const userData = yield call([response, 'json']); yield put({ type: FETCH_USER_SUCCESS, payload: userData }); } catch (error) { yield put({ type: FETCH_USER_FAILURE, payload: error.message }); } } function* rootSaga() { yield takeEvery('FETCH_USER', fetchUserSaga); } export default rootSaga;
- 然后,创建
reducer
(和上面Redux - Thunk
例子中的reducer
类似):// reducers.js const initialState = { user: null, error: null }; const userReducer = (state = initialState, action) => { switch (action.type) { case FETCH_USER_SUCCESS: return { ...state, user: action.payload }; case FETCH_USER_FAILURE: return { ...state, error: action.payload }; default: return state; } }; export default userReducer;
- 最后,在创建
store
时应用Redux - Saga
中间件:// store.js import { createStore, applyMiddleware } from 'redux'; import createSagaMiddleware from 'redux - saga'; import userReducer from './reducers'; import rootSaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); const store = createStore( userReducer, applyMiddleware(sagaMiddleware) ); sagaMiddleware.run(rootSaga);
- 作用:
- Redux - Logger
- 作用:
- 用于日志记录,它可以在
action
被分发、reducer
处理action
以及状态更新的过程中记录相关信息。这对于调试Redux应用非常有帮助,可以清楚地看到action
的流向和状态的变化情况。
- 用于日志记录,它可以在
- 实例:
- 定义
logger
中间件:// loggerMiddleware.js const loggerMiddleware = (store) => (next) => (action) => { console.log('dispatching', action); const result = next(action); console.log('next state', store.getState()); return result; }; export default loggerMiddleware;
- 在创建
store
时应用logger
中间件:// store.js import { createStore, applyMiddleware } from 'redux'; import loggerMiddleware from './loggerMiddleware'; import rootReducer from './reducers'; const store = createStore( rootReducer, applyMiddleware(loggerMiddleware) );
- 当
action
被分发时,例如在一个计数器应用中:// actions.js const INCREMENT = 'INCREMENT'; const increment = () => ({ type: INCREMENT });
// counterReducer.js const initialState = { count: 0 }; const counterReducer = (state = initialState, action) => { switch (action.type) { case INCREMENT: return { ...state, count: state.count + 1 }; default: return state; } }; export default counterReducer;
// index.js import React from 'react'; import ReactDOM from 'react - dom'; import { Provider } from 'react - redux'; import store from './store'; import Counter from './Counter'; ReactDOM.render( <Provider store={store}> <Counter /> </Provider>, document.getElementById('root') );
- 当点击按钮触发
increment
动作时,logger
中间件会在控制台打印出分发的action
({type: 'INCREMENT'}
)以及更新后的状态({count: 1}
等)。
- 当点击按钮触发
- 定义
- 作用:
原文地址:https://blog.csdn.net/m0_51244077/article/details/144788573
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!