您现在的位置是:网站首页> 编程资料编程资料

在 React 中使用 Redux 解决的问题小结_React_

2023-05-24 357人已围观

简介 在 React 中使用 Redux 解决的问题小结_React_

在 React 中使用 Redux 解决的问题

在 React 项目中未加入 Redux 时的问题

在 React 中组件通信的数据流是单向的,顶层组件可以通过 props 属性向下层组件传递数据,而下层组件不能直接向上层组件传递数据。要实现下层组件修改上层组件的数据,需要上层组件传递修改数据的方法到下层组件。当项目越来越大的时候,组件之间传递数据以及传递修改数据的方法变得越来越困难。

在这里插入图片描述

在 React 项目中加入 Redux 的好处

使用 Redux 管理数据,由于 Store 独立于组件,使得数据管理独立于组件,解决了组件与组件之间传递数据困难的问题。

在这里插入图片描述

React + Redux 安装 Redux

在 react 项目中使用 redux 要下载两个模块

npm install redux react-redux

React 中 Redux 的工作流程

在 React 中 Redux 的工作流程有些变化:

  1. 组件通过 dispatch 方法触发 Action
  2. Store 接收 Action 并将 Action 分发给 Reducer
  3. Reducer 根据 Action 类型对状态进行更改并将更改后的状态返回给 Store
  4. 组件订阅了 Store 中的状态,Store 中的状态更新会同步到组件

在这里插入图片描述

React 计数器案例

React 实现

创建项目安装模块

# 创建项目(React 17 版本) npx create-react-app myapp # 安装 redux cd myapp npm install redux

删掉无用的文件

├─ src │ ├─ App.css │ ├─ App.test.js │ ├─ index.css │ ├─ logo.svg │ ├─ reportWebVitals.js │ └─ setupTests.js

初步实现

// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux' const initialState = { count: 0 } function reducer (state = initialState, action) { switch (action.type) { case 'increment': return { count: state.count + 1 } case 'decrement': return { count: state.count - 1 } default: return state } } const store = createStore(reducer) const increment = { type: 'increment' } const decrement = { type: 'decrement' } function Counter() { return 
{store.getState().count}
} store.subscribe(() => { ReactDOM.render( , document.getElementById('root') ); }) console.log(store.getState()) ReactDOM.render( , document.getElementById('root') );

使用 Redux

开发时我们会把组件写在单独的文件中,如果将 Counter 组件单独提取,就无法访问 store 对象以及一些其它的问题,所以需要使用 redux。

安装 react-redux

npm install react-redux 

react-redux 用于让 react 和 redux 完美结合,它仅仅提供两个内容:

  • Provider 组件
    • 它可以将创建出来的 store 放到全局,允许所有组件访问
    • 可以解决将组件存储在单独文件中时,访问 store
  • connect 方法
  • connect 方法会帮助订阅 store,当 store 中的状态发生变化,会重新渲染组件
    • 解决了需要手动订阅更新组件状态
  • connect 方法可以获取 store 中的状态,映射到组件的 props 属性中
  • connect 方法可以获取 dispatch 方法,组件可以通过 props.dispatch访问

在这里插入图片描述

Provide 组件

  • Provider 组件要包裹项目中所有的组件,也就是应该被放在最外层组件上
  • Provider 通过 store 属性接收创建的 store 对象

connect 方法

  • 首先调用 connect 方法
    • 第一个参数是一个函数
      • 函数接收的第一个参数,即组件中的状态 state
      • 函数要返回一个对象,该对象定义的属性都会映射到组件的 props 属性中
    • 第二个参数也是一个函数
      • 函数接收的第一个参数,即 dispatch 方法
      • 函数要返回一个对象,可以定义触发 Action 的方法,它们同样会被映射到组件的 props 属性中
    • 返回一个方法
  • 继续调用返回的方法,并传递当前的组件

修改代码

// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux' import Counter from './components/Counter' import { Provider } from 'react-redux' const initialState = { count: 0 } function reducer (state = initialState, action) { switch (action.type) { case 'increment': return { count: state.count + 1 } case 'decrement': return { count: state.count - 1 } default: return state } } const store = createStore(reducer) ReactDOM.render( // 通过 provider 组件将 store 放在了全局,供所有组件可以访问 , document.getElementById('root') ); 

提取的 Counter 组件

// src/components/Counter.js import { connect } from 'react-redux' function Counter(props) { return 
{props.count}
} const mapStateToProps = state => ({ count: state.count }) export default connect(mapStateToProps)(Counter)

使用 connect 第二个参数简化组件

// src/components/Counter.js import { connect } from 'react-redux' function Counter({count, increment, decrement}) { return 
{count}
} const mapStateToProps = state => ({ count: state.count }) const mapDispatchToProps = dispatch => ({ increment () { dispatch({ type: 'increment' }) }, decrement () { dispatch({ type: 'decrement' }) } }) export default connect(mapStateToProps, mapDispatchToProps)(Counter)

使用 bindActionCreators 方法继续简化

触发 Action 的方法 incrementdecrement 的内容基本是一样的,redux 提供 bindActionCreators 方法生成一个函数,来简化这种重复性代码。

  • 参数
    • actionCreators: 对象或返回对象的函数
      • key是生成函数的名称
      • value是一个返回 Action 的函数
    • dispatch: 需传递Store 的 dispatch 方法
  • 返回一个对象,同 connect 接收的第二个参数
// src/components/Counter.js import { connect } from 'react-redux' import { bindActionCreators } from 'redux' function Counter({count, increment, decrement}) { return 
{count}
} const mapStateToProps = state => ({ count: state.count }) const mapDispatchToProps = dispatch => ({ ...bindActionCreators({ increment() { return { type: 'increment' } }, decrement() { return { type: 'decrement' } } }, dispatch) }) export default connect(mapStateToProps, mapDispatchToProps)(Counter)

此时还没有达到简化的效果,可以将 actionCreators 提取到单独的文件中。

// src\store\actions\counter.action.js export const increment = () => ({ type: 'increment' }) export const decrement = () => ({ type: 'decrement' })
// src/components/Counter.js import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as counterActions from '../store/actions/counter.action' function Counter({count, increment, decrement}) { return 
{count}
} const mapStateToProps = state => ({ count: state.count }) const mapDispatchToProps = dispatch => bindActionCreators(counterActions, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Counter)

代码重构

为了继续演示 Redux 的相关内容,将与 Redux 相关的代码从 src/index.js中抽离出去,让项目结构更加合理。

  • 将 reducer 函数抽离到一个文件中
  • 将创建 store 的代码手里到一个文件中
  • 将 Actions 的 type 抽象成模块中的成员,好处是:

    编辑器有提示,避免写错

    编辑器自动插入模块引入的代码

将 Actions 的 type 抽象成模块中的成员

// src\store\actions\counter.action.js import { DECREMENT, INCREMENT } from "../const/counter.const" export const increment = () => ({ type: INCREMENT }) export const decrement = () => ({ type: DECREMENT })

将 reducer 函数抽离到一个文件中

// src\store\reducers\counter.reducer.js import { DECREMENT, INCREMENT } from "../const/counter.const" const initialState = { count: 0 } function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { count: state.count + 1 } case DECREMENT: return { count: state.count - 1 } default: return state } } export default reducer 

将创建 store 的代码手里到一个文件中

// src\store\index.js import { createStore } from 'redux' import reducer from './reducers/counter.reducer' export const store = createStore(reducer)

修改后的 src/index.js

提示: 本文由整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!

-六神源码网