欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > React 之 Redux =》 理解+应用

React 之 Redux =》 理解+应用

2025/2/24 0:48:39 来源:https://blog.csdn.net/m0_51244077/article/details/144788573  浏览:    关键词:React 之 Redux =》 理解+应用

文章目录

  • Redux基础介绍
    • 一、概述
    • 二、元素组成
      • 1. Action(动作)
      • 2. Reducer(纯函数)
      • 3. Store(仓库)
    • 三、原理结构
    • 四、场景应用
      • 1. 大型复杂的单页应用(SPA)
      • 2. 多用户协作的应用
      • 3. 数据持久化和缓存管理
    • 五、实例
      • 1. 简单的计数器应用
      • 2. 待办事项列表应用
    • Redux 之 React
    • 在React中使用Redux
      • 一、安装Redux相关库
      • 二、创建Redux Store和相关逻辑
      • 三、在React组件中使用Redux
    • 类组件connect具体应用
      • **导出复用原理**
      • **实例**
      • 1. 安装依赖
      • 2. 创建Redux相关文件
        • actions.js
        • reducers.js
        • store.js
      • 3. 创建React类组件
        • Product.js
        • CartItem.js
        • Cart.js
        • App.js
      • 4. 总结
    • 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。根据actiontype来决定如何更新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;}
}
  • 特点
    • 纯函数特性:给定相同的输入(相同的stateaction),总是返回相同的输出。没有副作用,如不会修改传入的参数,不会进行异步操作,也不会直接操作DOM。
    • 不可变性:在更新state时,应该返回一个新的对象,而不是直接修改原有的state。这有助于追踪状态的变化,并且在React等视图层中可以更高效地检测组件是否需要重新渲染。

3. Store(仓库)

  • 定义:是一个对象,它将actionreducer联系在一起。通过createStore函数创建(在Redux核心库中)。例如:
import { createStore } from 'redux';
const store = createStore(todoReducer);
  • 功能
    • 保存状态:它存储着整个应用的状态树。可以通过store.getState()方法获取当前的状态。
    • 分发动作:提供store.dispatch(action)方法,用于将action发送给reducer,从而触发状态的更新。
    • 订阅更新:通过store.subscribe(listener)方法,可以订阅状态的变化。当状态更新时,会调用listener函数,通常用于更新UI等操作。

三、原理结构

Redux的工作流程是单向数据流:

  1. 动作触发:视图层(如React组件)通过用户交互或者其他事件(例如点击按钮)触发一个action
  2. 动作分发:这个actionstore.dispatch()方法分发到reducer
  3. 状态更新reducer根据传入的action和当前state,计算并返回一个新的state。这个新的state会替换掉旧的state存储在store中。
  4. 通知订阅者store会通知所有订阅了状态变化的监听器(通过store.subscribe()注册的函数)。这些监听器通常会更新视图层,使得UI能够反映最新的状态。

四、场景应用

1. 大型复杂的单页应用(SPA)

  • 在大型SPA中,应用状态可能会非常复杂,涉及多个模块和组件之间的交互。例如,一个电商单页应用,有商品列表展示、购物车管理、用户登录状态等多个功能模块。
  • 商品列表:当用户进行搜索操作时,会触发一个SEARCH_PRODUCTS类型的actionreducer会根据这个action更新商品列表的状态。
  • 购物车管理:添加商品到购物车会触发ADD_TO_CART类型的action,从购物车移除商品会触发REMOVE_FROM_CART等动作,这些动作通过reducer来更新购物车状态。Redux可以很好地管理这些复杂的状态变化,确保每个模块的状态更新是可预测的。

2. 多用户协作的应用

  • 考虑一个实时文档编辑应用,多个用户可以同时编辑一个文档。
  • 当一个用户输入内容时,会触发UPDATE_DOCUMENT类型的action。Redux可以协调各个用户的操作,通过服务器同步数据,使得每个用户看到的文档状态都是最新的。reducer会根据这些操作更新文档的内容状态,并且通过store的订阅机制,及时更新每个用户的视图。

3. 数据持久化和缓存管理

  • 对于一些需要离线支持或者频繁请求相同数据的应用。例如,一个新闻阅读应用,它可以使用Redux来管理已缓存的新闻文章。
  • 当应用首次加载新闻文章时,会触发FETCH_ARTICLES类型的actionreducer更新状态来存储获取到的文章列表。如果应用离线,它可以从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相关库

  1. 首先需要安装reduxreact-redux库。
    • 如果使用npm,可以在项目目录下的终端中运行以下命令:
    npm install redux react-redux
    
    • 如果使用yarn,则运行:
    yarn add redux react-redux
    

二、创建Redux Store和相关逻辑

  1. 定义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};
    };
    
  2. 创建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;
    
  3. 创建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

  1. 使用Provider组件包裹根组件
    • index.js文件中,将整个React应用包裹在Provider组件内,使得应用中的所有组件都可以访问Redux store。Providerreact - 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')
    );
    
  2. 在组件中连接Redux(使用connect函数)
    • 功能性组件(使用useSelectoruseDispatch
      • 对于功能性组件,可以使用react - redux提供的useSelectoruseDispatch钩子函数来获取状态和分发动作。例如,在一个计数器组件中:
      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中。例如,在上面的类组件中,mapStateToPropsstate.count映射为组件的count属性。
    • mapDispatchToProps函数(或对象)
      • 这个函数(或对象)用于将动作创建函数映射到组件的props中,使得组件可以通过props来分发动作。在类组件的例子中,mapDispatchToProps是一个对象,它将incrementdecrement动作创建函数直接作为属性,这些属性会被添加到组件的props中,从而可以在组件中通过this.props.incrementthis.props.decrement来分发动作。在功能性组件中,useDispatch钩子函数返回的dispatch函数用于手动分发动作,更加灵活。

类组件connect具体应用

导出复用原理

  • connect函数的作用机制
    • 在React - Redux中,connect函数是一个高阶函数,用于将React组件与Redux的store连接起来。它接收两个参数:mapStateToPropsmapDispatchToProps(这两个参数也可以是函数或者对象,用于不同的映射方式)。
    • mapStateToProps原理:这个函数用于将Redux store中的状态映射到组件的props中。它会在store状态发生变化时被调用,每次调用都会重新计算返回的对象。返回对象中的属性会作为props传递给组件。例如,如果mapStateToProps返回{ count: state.count },那么组件就会通过props.count来访问Redux中的count状态。
    • mapDispatchToProps原理:这个函数(或对象)用于将Redux中的action创建函数映射到组件的props中。当它是一个函数时,接收dispatch作为参数,返回一个对象,对象中的属性是action创建函数,这些函数内部会调用dispatch来分发action。当它是一个对象时,对象中的属性直接就是action创建函数,connect函数会自动将它们绑定到dispatch上,这样组件就可以通过props来调用这些action创建函数。
  • 组件复用原理
    • 当一个通过connect函数连接了Redux的组件被导出后,它可以在多个地方被复用。因为mapStateToPropsmapDispatchToProps的映射关系是基于组件的使用场景和Redux的store结构来定义的,所以只要Redux store的结构和组件所需的状态及action保持一致,组件就可以在不同的地方复用。例如,一个显示用户信息的组件,它通过mapStateToProps获取用户状态,通过mapDispatchToProps获取更新用户信息的action函数,只要在其他地方也有相同的Redux store结构和对用户信息的操作需求,这个组件就可以直接复用。

实例

1. 安装依赖

确保项目中安装了 reduxreact-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 组件中复用 ProductCart 组件,展示商品列表和购物车。

// 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 中间件应用及介绍

  1. Redux-Thunk
    • 作用
      • 用于处理异步操作。它允许action创建函数返回一个函数,而不是一个普通的action对象。这个返回的函数可以接收dispatchgetState作为参数,从而在函数内部执行异步操作(如网络请求),并根据异步操作的结果来分发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)
        );
        
  2. 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);
        
  3. 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}等)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词