React + Redux

在recat中不应用redux 时遇到的问题

在react中组件通信的数据是单向的,顶层组件能够通过props属性向上层组件传递数据,而上层组件不能向下层组件传递数据,要实现上层组件批改数据,须要下层组传递批改数据的办法到上层组件,当我的项目越来越的时候,组件之间传递数据变得越来越艰难

在react中退出redux 的益处

应用redux治理数据,因为Store独立于组件,使得数据管理独立于组件,解决了组件之间传递数据艰难的问题

应用redux

下载redux

npm install redux react-redux

redux 工作流程

  1. 组件通过 dispatch 触发action
  2. store 承受 action 并将 action 分发给 reducer
  3. reducer 依据 action 类型对状态进行更改并将更改后的数据返回给store
  4. 组件订阅了store中的状态,store中的状态更新会同步到组件

应用react+redux实现计数器

  1. 创立我的项目,并装置 redux
# 如果没有装置react脚手架则执行这条命令装置reate脚手架npm install -g create-react-app# 创立reate我的项目create-react-app 我的项目名# 进入我的项目cd 我的项目名# 装置 reduxnpm install redux reate-redux
  1. 引入redux,并依据开始实现的代码在react中实现计数器
//index.jsimport React from 'react';import ReactDOM from 'react-dom';import App from './App';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 Count() {  return <div>    <button onClick={() => store.dispatch(increment)}>+</button>    <span>{store.getState().count}</span>    <button onClick={() => store.dispatch(decrement)}>-</button>  </div>}store.subscribe( () => {  console.log(store.getState())  ReactDOM.render(    <React.StrictMode>      <Count />    </React.StrictMode>,    document.getElementById('root')  );})ReactDOM.render(  <React.StrictMode>    <Count />  </React.StrictMode>,  document.getElementById('root'));

显著以上形式尽管能够实现计数器的性能,但在理论我的项目中必定不能这样应用,因为组件个别都在独自的文件中的,这种形式显著在其余组件中并不能获取到Store。

计数器案例代码优化-让store全局可拜访

为了解决Store获取问题须要应用react-redux来解决这个问题,react-redux给咱们提供了Provider组件和connect办法

  • Provide 组件
是一个组件 能够吧创立进去的store 放在一个全局的中央,让组件能够拿到store,通过provider组件,将 store 放在了全局的组件能够够的到的中央 ,provider要求咱们放在最外层组件
  • connect

connect 帮忙咱们订阅store中的状态,状态产生扭转后帮忙咱们从新渲染组件

通过 connect 办法咱们能够拿到 store 中的状态 把 store 中的状态映射到props中

通过 connect 办法能够拿到 dispatch 办法

connect 的参数为一个函数 这个函数能够拿到store中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性

connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知connect须要映射到那个组件的props

  1. 新建 Component 文件夹、创立 Count.js 文件
import React from 'react'function Count() {    return <div>        <button onClick={() => store.dispatch(increment)}>+</button>        <span>{store.getState().count}</span>        <button onClick={() => store.dispatch(decrement)}>-</button>    </div>}export default Count
  1. 引入 Provider 组件搁置在最外层,并制订store
ReactDOM.render(  // 通过provider组件 将 store 放在了全局的组件能够够的到的中央  provider要求咱们放在最外层组件  <Provider store={store}><Count /></Provider>,  document.getElementById('root'));
  1. 引入 connect 办法 依据 connect 的应用来包裹组件
const mapStateProps = state => ({    count: state.count,    a: '1'})// connect 的参数为一个函数 这个函数能够拿到store中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性// connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知connect须要映射到那个组件的propsexport default connect(mapStateProps)(Count)
  1. 革新 Count 组件把 action 复制到该文件中
const increment = {    type: 'increment'}const decrement = {    type: 'decrement'}function Count({count,dispatch}) {    return <div>        <button onClick={() => {dispatch(increment)}}>+</button>        <span>{count}</span>        <button onClick={() => {dispatch(decrement)}}>-</button>    </div>}

当初我的项目曾经能够运行了然而Count组件中的 提交Action的那一长串代码影响视图的可读性,所以代码还是须要优化

计数器案例代码优化-让视图中的代码可读性更高

咱们心愿视图中间接调用一个函数这样视图代码可读性强,这个须要利用connect的第二个参数,第二个参数是一个函数,这个函数的形参就是dispatch办法,要求这个函数返回一个对象,返回的这个对象中的内容都会映射到组件的props属性上

  1. 申明一个变量为connect中的第二个参数,在这个变量中返回执行不同action操作的对象
// connect 的第二个参数 这个参数是个函数 这个函数的形参就是dispatch办法 要求返回一个对象 这个对象中的属性会被映射到组件的props上const mapDispatchToProps = dispatch => ({    increment (){        dispatch({            type: 'increment'        })    },    decrement (){        dispatch({            type: 'decrement'        })    }})// connect 的参数为一个函数 这个函数能够拿到store中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性// connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知connect须要映射到那个组件的propsexport default connect(mapStateProps, mapDispatchToProps)(Count)
  1. 在组件中构造props在视图中间接绑定事件
function Count({count,increment,decrement}) {    return <div>        <button onClick={increment}>+</button>        <span>{count}</span>        <button onClick={decrement}>-</button>    </div>}

通过这次优化咱们发现 调用 dispatch 触发action 的办法的代码都是反复的,所以还须要持续优化

优化调用 dispatch 触发action 的办法的反复代码简化

利用 bindActionCreators 来简化 dispatch 触发 action的操作,bindActionCreators来帮忙咱们生成执行action动作的函数

bindActionCreators 有两个参数,第一个参数为 执行action的对象,第二个参数为 dispatch办法

  1. 拆散action操作,新建store/actions/counter.actions.js文件把执行action操作独自放在这个文件并导出
export const increment = () => ({type: 'increment'})export const decrement = () => ({type: 'decrement'})
  1. 在Count.js中导入对于计数器的action,用bindActionCreators办法来生成dispatch执行action函数
import { bindActionCreators } from 'redux'import * as counterActions from './../store/actions/counter.actions'const mapDispatchToProps = dispatch => (bindActionCreators(counterActions, dispatch))// connect 的参数为一个函数 这个函数能够拿到store中的状态,要求咱们这个函数必须返回一个对象,在这个对象中写的内容都会映射给组件的props属性// connect 调用后返回一个函数 返回的这个函数持续调用须要传入组件通知connect须要映射到那个组件的propsexport default connect(mapStateProps, mapDispatchToProps)(Count)

代码优化到这里咱们发现,redux的代码与组件交融在一起,所以我须要拆分成独立的,为什么要抽离redux呢?因为咱们要让咱们的代码构造更加正当

重构计数器,把redux相干代码抽离

把reducer函数抽离为独自的文件、把创立store抽离到独自的文件中

  1. 因为在reducer 和 actions中咱们都写了字符串,然而字符串没有提醒所以咱们把字符串定义成常量避免咱们呈现单词谬误这种低级谬误,新建 src/store/const/counter.const.js 文件
export const INCREMENT = 'increment'export const DECREMENT = 'decrement'
  1. 新建 src/store/reducers/counter.reducers.js 文件把 reducer 函数抽离到此文件中
import { INCREMENT, DECREMENT} from './../const/counter.const'const initialState = {    count: 0}// eslint-disable-next-line import/no-anonymous-default-exportexport default (state = initialState, action) => {    switch (action.type) {        case INCREMENT:            return {                count: state.count + 1            }        case DECREMENT:            return {                count: state.count - 1            }        default:            return state    }}
  1. 更改actions中的字符串为引入变量
import { INCREMENT, DECREMENT} from './../const/counter.const'export const increment = () => ({type: INCREMENT})export const decrement = () => ({type: DECREMENT})
  1. 创立src/store/index.js文件 ,在这个文件中创立store 并导出
import { createStore } from 'redux';import reducer from './reducers/counter.reducers'export const store = createStore(reducer)
  1. 在引入store的文件中扭转为冲我的项目中store文件中引入store
import React from 'react';import ReactDOM from 'react-dom';import Count from './components/Count';import { store } from './store'import { Provider } from 'react-redux'/** * react-redux 让react 和 redux 完满联合*    Provider 是一个组件 能够吧创立进去的store 放在一个全局的中央 让组件能够拿到store*    connect  是一个办法 */ReactDOM.render(  // 通过provider组件 将 store 放在了全局的组件能够够的到的中央  provider要求咱们放在最外层组件  <Provider store={store}><Count /></Provider>,  document.getElementById('root'));

为action 传递参数,对计数器案例做扩大

这个计数器案例曾经实现了点击按钮加一减一操作了,当初有个新需要咱们须要加减一个数值例如加五减五

这就须要对action传递参数了

  1. 在视图中按钮绑定函数传入参数
function Count({count,increment,decrement}) {    return <div>        <button onClick={() => increment(5)}>+</button>        <span>{count}</span>        <button onClick={() => decrement(5)}>-</button>    </div>}
  1. 在dispacth执行action动作时承受参数并传入到action中
export const increment = payload => ({type: INCREMENT, payload})export const decrement = payload => ({type: DECREMENT, payload})
  1. 在reducers中接管参数并作相应解决
export default (state = initialState, action) => {    switch (action.type) {        case INCREMENT:            return {                count: state.count + action.payload            }        case DECREMENT:            return {                count: state.count - action.payload            }        default:            return state    }}
原文地址:https://kspf.xyz/archives/10/