Redux

342 阅读8分钟

1. 为什么需要redux

  1. 组件之间的通信,数据流是单项的,顶层组件向下传递数据,通过【props】,底层组件是不可以向上层组件传递数据,如果底层组件想修改数据,只能通过顶层组件向下传递事件,底层组件触发事件,修改数据,当项目越来越大,组件之间的数据传递会比较繁琐。

  2. 【redux】在react中担任一个数据管理模块

  3. 【redux使用场景】

    • 同一个【state】需要多个Component中进行共享
    • 某一个组件的状态,需要进行共享
    • 某一个状态需要在任何的地方都可以拿到
    • 一个组件需要改变另一个组件的状态
    • 一个组件需要改变全局状态
  4. 【redux】安装

npm install redux -S
npm install react-redux -S
  1. 【组件状态state选择那种方式进行管理】
    • UI库相关的组件状态,在组件的内部进行保存
    • 需要共享的【state】放在redux里面进行管理
    • 像后端请求数据,放到【redux】里面进行管理

2. redux工作流程 待更新

3. store的创建

  1. 【store】是单独存在的,不存在于任何组建内,所以store在创建的时候,是单独放在一个文件内
【 redux 提供了一个函数,他用来生成store 】

import { createStore } from 'redux'

function reducer() { 【 创建一个reducer 】
  return {
  	count: 0
  }
}

const store = createStore(reducer) 【 createStore接收一个纯函数,也就是reducer 】

【 获取store里面的值 】
console.log(store.getState())

3.1 如何将store传递给组件,并组件获取使用

  1. 由于【 store 】与组件是分开的,并没有什么联系,所以想要进行数据传递,需要使用【react-redux】实现数据传递
【 src/index.jsimport { Provider } from 'react-redux' 【 用导出的方法,将组件进行包裹,使用 】

ReactDOM.render(
  <React.StrictMode>
    <Provider store={ store }> <App /></Provider> 【 在Provider,属性=属性名的方式将store向下传递 】
   
  </React.StrictMode>,
  document.getElementById('root')
)


【 组件中获取store 】
import React from 'react'
import { connect } from 'react-redux' 【 使用 connect方法获取数据】

function App (props) { 【 03.  组件要通过props来获取store里面的数据 】
  return (
  	<div>{props.count}</div>
  )
}

/******获取数据*******/02. connect的回调函数接受一个参数state,也就是store传递过来的数据,store里面的数据来源于reducer 】
const mapStateToProps = (state) => ({ 
	count: state.count
})

/*********************/01. connect调用两次,第一次调用是拿到数据,第二次调用是要讲数据传给哪一个组件 】
export default connect(mapStateToProps)(App)

3.2 更改store数据(action)

  1. 【 action 】本身是一个对象类型,它里面有有一个【type】属性,他是一个字符串类型,他类似一个自定义事件
  2. 组件通过获取【store】,接受到store里面包含数据以及一个方法,该方法是【dispath】,它用于触发提交【action】,组件通过触发dispath,然后【reducer】接收到【action】,并进行操作
【 组件内部 】
import React from 'react'
import { connect } from 'react-redux'
function App (props) {
  console.log(props);
  return (
    <div>
      <span>{props.count}</span>
      【 dispath的内部参数就是action 】
      <button onClick={() => {props.dispatch({ type: 'button' })}}>点击按钮</button>
    </div>
  )
}
const mapStoreToState = (state) => ({
  count: state.count
})
export default connect(mapStoreToState)(App)

【 reducer 】
const item = {
  count: 0
}
const reducer =(state = item , action) => {
  if ( action.type === 'button' ) {
    return {
      count: state.count += 1
    }
  }else {
    return {
      count: state.count
    }
  }
}

export default reducer

3.3 action的第二个参数

  1. action的第二个参数是【 content 】,当组件触发dispatch的时候,想给【reducer】传递数据,这时候需要给action设置第二个参数
【 组件传递 】
<button onClick={() => {props.dispatch({ type: 'button',payload: 5 })}}>点击按钮</button>
【 reducer收到 】
const reducer =(state = item , action) => {
	console.log(action.payload)
}

4. 简化action

  1. 在组件内部,在元素身上去调用action,一是不方便,二是书写的长度比较长,所以将action提取成函数
  2. 通过使用【connect】的第二个参数,实现将action提取成函数
import React from 'react'
import { connect } from 'react-redux'

function App (props) {
  console.log(props);
  return (
    <div>
      <span>{props.count}</span>
      <button onClick={() => {props.bts({payload:5})}}>点击按钮</button> 【 在使用的时候,connect会生成好方法,可以通过props去调用方法 】
    </div>
  )

}
const mapStoreToState = (state) => ({
  count: state.count
})
const mapStoreDispatch = (dispatch) => ({
  btn() {
    dispatch({type: 'button'}) 【 无参数 】
  },
  bts(payload) {
    dispatch({type: 'button',...payload})  【 有参数 】
  } 
})

export default connect(mapStoreToState,mapStoreDispatch)(App)
  1. 在进行简化,将【 action 】放在单独的文件内部,结合【redux】里面的【bindActionCreators】
【 action单独存放在文件内部 】
export const BTN = () =>({ type: 'button' }) 【 无参数 】
export const BTNs = (payload) =>({ type: 'button',payload }) 【 有参数 】


【 组件里 】
import { bindActionCreators } from 'redux'
import React from 'react'

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'

import { BTN } from './action'

function App (props) {
  console.log(props);
  return (
    <div>
      <span>{props.count}</span>
      <button onClick={() => {props.BTN()}}>点击按钮</button>
    </div>
  )

}

const mapStoreToState = (state) => ({
  count: state.count
})


const mapStoreDispatch = (dispatch) => ({
  ...bindActionCreators({BTN},dispatch)
})

export default connect(mapStoreToState,mapStoreDispatch)(App)
  1. 【bindActionCreators】通过使用redux内部的方法,设置自动生成action触发函数,

    • 方法是内部接收两个参数,参数一是一个对象,参数二是dispatch
    • bindActionCreators({ },dispatch)

4.1 action常量设置

  1. 将type的值拿出来定义成常量,原因是因为这个type的值在多个地方进行使用,也是避免在之后书写时,没有语法提示,容易出错,所以将type类型的值定成常量
【 action_type.jsexport const INCREMENT ='increment'
export const INCREMENT_N ='increment_n'

【 reducer内部引入使用 】
import { INCREMENT,INCREMENT_N } from './action_type.js'
const item = {
  count: 0
}
const reducer =(state = item , action) => {
  if (action.type === INCREMENT) {
    return {
    	count: state.count+=1  
    }    
  }
}

export default reducer
【 action定义的地方引入 】
import { INCREMENT,INCREMENT_N } from './action_type.js'

export const BTN = () =>({ type: 'INCREMENT' }) 【 无参数 】

5. reducer的拆分与合并

  1. 【reducer】的类型是一个纯函数类型,而【reducer】是处理数据的地方,返回他返回的结果会传递【store】,进行保存
  2. 【 reducer 】他与【store】是单独存放的,并且他们是分开存放的【 Store/reducer/index.js 】
【 多个reducer进行合并 redux里面的combinerReducers方法 】

import { combinerReducers } from 'redux'

import contentReducer from './content.reducer.js' /*  reducer文件
import payloadReducer from './content.reducer.js' */

export default combinerReducers({
	content: contentReducer,
  payload: payloadReducer
})

【combinerReducers返回的数据格式  { counter: {count},person:[{}] }】

【 index.js 里面的引入需要进行更改 】
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { Provider } from 'react-redux'

import reducer from './reducer/index.js' 【 将合并后的reducer导入进来,传入store里面 】
import { createStore } from 'redux'

const store = createStore(reducer)

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}><App /></Provider>
  </React.StrictMode>,
  document.getElementById('root')
);



【 组件中获取,通过connect第一个参数获取想要拿到的数据 】

const mapStoreToState = (state) => ({  【 state打印的结果是一个对象组成的,通过打点,拿到想要的数据即可 】
  count: state.content
})

6. redux中间件

6.1 添加中间件后redux的工作流程

6.2 中间件的使用

  1. 【为什么要使用中间件】比如说在项目开发过程中,数据不是同步获取的,或者是在处理数据之前有一些异步操作需要完成,这时,通过action发送的事件传动到中间件,让中间件进行处理,然后在返回给store,进行使用。
  2. 【什么时候会触发中间件】,当组件Components触发【action】的时候,就会去执行中间件
  3. 【注册中间件 applyMiddleware 】applyMiddleware是一个方法
【 index.jsimport React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import { createStore,applyMiddleware } from 'redux'

function reducer( state,action ) {
    return state
}
// 定义一个中间件函数
function middle() {
    // 在内部,规定需要返回一个函数
    return function () {
        // 异步处理函数,需要返回一个函数
        return function () {
            // 这里就是,当action被触发的时候,触发,此处是完成异步操作
            
        }
    }
}
【 applyMiddleware注册一个中间件,想要哪一个函数成为中间件,就需要将函数传给applyMiddleware,完成注册 】
const store = createStore(reducer,applyMiddleware(middle)) 

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}><App /></Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

6.3 redux-saga异步解决方案

  1. 下载【npm install redux-saga】
  2. 【redux-saga】基本使用
【 index.jsimport { createStore,applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga' 【 createSagaMiddleware进行调用,返回值才能作为中间件 】
import loadSaga from './store/saga.js' 【 处理过的saga导入到index.js 里面】

const sagaMiddleware = createSagaMiddleware()
const store = createStore(toList,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(loadSaga) 




【 saga如何截取action 】
import { takeEvery, put } from 'redux-saga/effects' 【 redux-saga提供了两个方法 】
	1. takeEvery 截取action指令
	2. put 相当于dispatch,向后重新发送一个指令

【 由于redux-saga 的底层是基于es6中的generator 】

function* loadPerson () { 【 同为generator语法 】
	let data = yield axios.get()  // 发送一个请求,拿到请求的数据data
  
  yield put({ type: 'load_success',data }) 【 请求成功发送请求,再次触发一个action,将数据传递给reducer 】
  
}

export default function* personSaga () {
 【 需要了解的是,takeEvery拦截一个指令,然后要在重新创建一个函数,来处理拦截之后要做的事情 】
  yield takeEvery( 'load_person', loadPerson ) 
}


【 reducer里面的接收action,完成一些操作 】

6.4 redux-saga的合并

【 redux-saga/effects 下的all方法 】
import { all } from 'redux-saga/effects'

import personSage from './person.saga'
export default function* rootSaga () {
  yield all([
    personSage() //多个文件直接逗号进行书写即可
  ])
}

6.5 redux-saga基本使用练习

【 组件 】


【 action 】


【 reducer 】


【 saga 】


【 index.js

6.6 action与reducer的优化

  1. 通过下载【redux-actions】包,来优化action与reducer 【 npm i redux-actions 】
【 action的优化 】

import { createAction } from 'redux-actions'
export const increment_action = createAction('increment_action')【 createAction返回的是action信息对象 】



【 reduce的优化 】

// 如果需要和当前配置高名相同,可以改方法起别名进行使用
import { handleActions } from 'redux-action' 【 handleAction as countReducer 别名的方式 】
import { increment_action } from 'action配置路径' 【 action指令 】

const initText = { 【 初始化数据 】
    const :10
}

1. handleActions呦两个参数,第一个参数是对象,里面是键值对
2. 第二个参数是初始化数据

const createReducer = handleActions({
	[increment_action]: () => ({  【 键 接收的是哪一个指令,值就是当触发指令要做什么,是一个函数 】
                              	【 函数呦两个参数,第一个参数当前初始化数据state,
                              		 第二个参数是action,方便指令传参使用
                              	 】
  })
},initText)

export default createReducer