zoukankan      html  css  js  c++  java
  • React之redux学习日志(redux/react-redux/redux-saga)

    redux官方中文文档:https://www.redux.org.cn/docs/introduction/CoreConcepts.html

    react-redux Dome:https://codesandbox.io/s/react-redux-e1el3(需FQ才能访问)

    1. Redux工作流程图:

    2. redux三大原则:

      1. 单一数据源:在Redux中有且只能有一个 state 仓库

      2. State是只读的: state仓库的数据只能读取,不能进行修改

      3. 使用纯函数执行修改:reducer中,应该返回一个纯函数,函数接受先前的 state和action, 然后返回一个新的 state

    3. Redux 搭配 React 使用

    安装:

    npm install --save react-redux

          3.1. react-redux在React中的使用方式

      · 在react入口文件中注入Redux

    import React from 'react'
    import ReactDOM from 'react-dom'
    import RouterConfig from '@/Router'
    import { Provider } from 'react-redux'
    import store from '@/store'
    
    const App = () => (
      <div>
       <!-- Provider 让所有容器组件都可以访问 store -->
        <Provider store={store}>
          <RouterConfig/>  
        </Provider>
      </div>
    )
    
    const domContainer = document.querySelector('#app')
    ReactDOM.render(<App />, domContainer)
    

      · 根目录中新建store目录,并且添加:index.js、 reducer.js、create-action、action-type

           

      index.js文件

    import 'babel-polyfill' // es6解析
    import {
      createStore,
      compose,
      applyMiddleware
    } from 'redux'
    import reducer from './reducer'
    
    
    // redux-dev-tools工具配置
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
    
    const store = createStore(
      reducer,
      composeEnhancers(
        applyMiddleware(
        // 这里可以放一些中间件,如 redux-saga 等
        )
      )
    )
    
    export default store
    

      create-action.js  / action-type.js

    // action-type: 一般统一在这个文件中定义 action 的类型,方便管理
    export const GET_USERINFO_ACTION = 'GET_USERINFO_ACTION'
    
    // create-action:每个action都返回一个纯对象,type是约定必须
    import { GET_USERINFO_ACTION  } from './action-type'
    export const getUserInfoAction = (value) => ({
        type: GET_USERINFO_ACTION,
        value
    })
    

      reducer.js

    import { GET_USERINFO_ACTION  } from './action-type'
    // 创建一个默认的仓库,一般提出来会更加清晰
    const defaultState = { userInfo: {} }
    // reducer必须是一个纯函数 const reducer = (state=defaultState, action) => { const { type } = action
    // 深拷贝,一定不能直接对state进行修改 const newState = JSON.parse(JSON.stringify(state)) if (type === GET_USERINFO_ACTION){ newState.userInfo = action.value } return newState }  

    至此,仓库已经搭建完毕,接下来是在react中进行使用。

    上面已经在react中入口文件中注入了react,接下创建一个组件来对redux进行简单的使用

    新建 ReduxTest 组件

    import React, { Component, Fragment } from "react";
    import stroe from "./stroe";
    import { getUserInfoAction} from "./stroe/action-creators";
    
    class TestRedux extends Component {
      constructor(props) {
        super(props)
    
        this.handleUpdateUserInfoClick = this.handleUpdateUserInfoClick.bind(this)
      }
    
      handleUpdateUserInfoClick() {
    // 创建一个action,然后reducer会进行对于的处理,然后返回一个新的 state const action = getUserInfoAction({name: 'del lee'}) stroe.dispatch(action) } render() { return ( <Fragment> <button onClick={this.handleUpdateUserInfoClick}>跳转</button> </Fragment> ) } } export default TestRedux

      

     4. react-redux 在react中的使用

      结合上面的内容,我们修改一下ReduxTest组件

    import React, { Component, Fragment } from "react";
    import stroe from "./stroe";
    import { getUserInfoAction} from "./stroe/action-creators";
    
    // 引入 connect
    import { connect } from "react-redux";
    
    class TestRedux extends Component {
      constructor(props) {
        super(props)
    
        // this.handleUpdateUserInfoClick = this.handleUpdateUserInfoClick.bind(this)
      }
    
      // handleUpdateUserInfoClick() {
      //   创建一个action,然后reducer会进行对于的处理,然后返回一个新的 state
      //   const action = getUserInfoAction({name: 'del lee'})
      //   stroe.dispatch(action)
      //}
    
      render() {
        return (
          <Fragment>
    <!--      <button onClick={this.handleUpdateUserInfoClick}>跳转</button> -->
    
          <button onClick={this.props.handleUpdateUserInfoClick}>跳转</button>
          </Fragment>
        )
      }
    }
    
    const mapStateToProps = state => ({
       userInfo: state.userInfo
    })
    
    const mapDispatchToProps = (dispatch) => ({
       handleUpdateUserInfoClick: ()=> {
            const action = getUserInfoAction({name: 'del lee'})
            dispatch(action) // 执行action
       }
    })
    
    
    // export default TestRedux
    
    // 修改为,connect会将 mapStateToProps 与 mapDispatchToProps中的内容链接到 TestRedux 组件的props中
    // mapStateToProps 会接受到 state 仓库中所有的值
    // mapDispatchToProps: 会接受到 dispatch 方法
    export default connect(mapStateToProps, mapDispatchToProps)(TestRedux )    
    

    备注:为了确保redux中的state不能够直接修改其中的值和统一数据格式,一般建议结合  immutable.js 使用

               具体需查阅官方文档:https://immutable-js.github.io/immutable-js/docs/#/

    示例: 修改 reducer.js 文件

    import { GET_USERINFO_ACTION  } from './action-type'
    import { fromJS } from "immutable";
    
    // 创建一个默认的仓库,一般提出来会更加清晰
    //const defaultState = {
    //  userInfo: {}
    //}
    
    // 转换为 immutable 数据格式
    const defaultState = fromJS({
      userInfo: {}
    })
    
    // reducer必须是一个纯函数
    const reducer = (state=defaultState, action) => {
       const { type } = action
       // 深拷贝,一定不能直接对state进行修改
       // const newState = JSON.parse(JSON.stringify(state))  immutable数据格式不需要进行深拷贝
       
       if (type === GET_USERINFO_ACTION){
          // newState.userInfo = action.value  不能直接修改值
          // 使用set方法对值进行修改,会返回一个新的immutable对象
          state.set('userInfo', action.value)
       }
       
       return state  // 若不匹配直接返回原来的state即可
       // return newState
    }  

      还需要修改 ReduxTest 中 mapStateToProps 的获取方式

    ......
    
    const mapStateToProps = state => ({
    // userInfo: state.userInfo 会抛出异常
    // 使用get或者getIn获取state中的值 userInfo: state.get('userInfo') // or // userInfo: state.getIn(['userInfo']) }) ......

      
    5. Redux-Saga中间件

    redux-saga中文文档地址:https://redux-saga-in-chinese.js.org/docs/basics/DeclarativeEffects.html

    当我们需要执行一些异步操作时,由于action中只能返回一个对象,从而需要借助一些中间件来达到目的,redux-thunk 和 redux-saga是常见的两种中间件。

      redux-thunk 主要是使action能够返回一个函数而达到目的,这样导致了action函数变得复杂

      redux-saga 可以将异步操作单独分离出来封装到某些模块,这样保证action函数更加干净

    redux-saga的引入:

      修改 store/index.js 文件

    import 'babel-polyfill' // es6解析
    import {
      createStore,
      compose,
      applyMiddleware
    } from 'redux'
    import reducer from './reducer'
    
    // 需要在 store 目录中创建  sagas.js 文件
    import testSaga from "./sagas";
    import createSagaMiddleware from "redux-saga";
    
    // 创建 redux-saga 中间件
    const sagaMiddleware = createSagaMiddleware();
    
    // redux-dev-tools工具配置
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
    
    const store = createStore(
      reducer,
      composeEnhancers(
        applyMiddleware(
            sagaMiddleware // 引入saga中间件
        )
      )
    )
    
    // 运行saga
    sagaMiddleware.run(testSaga)
    
    export default store        
    

      在 store 中新增 sagas.js 文件

    import { call, put, takeEvery } from "redux-saga/effects"
    // 你可以写一个异步的接口或者一个异步的函数
    import { getUserInfoApi } from './api'
    // 可以在create-action.js中新增一个 action:   updateUserInfoAction
    import { updateUserInfoAction } from 'create-action'
    
    /** 在create-action.js新增一个action(当然在reducer.js中也要对state就行对应的操作)
    * export const updateUserInfoAction = (value) => ({
    *  type: 'UPDATE_USERINFO_ACTION',
    *  value
    *})
    */
    // saga 函数接受 action
    function* getUserInfoSaga(action) {
        // 声明 effects 函数 call:发起一次请求   call([api, [args]]),args是请求的参数
        const res = yield call(getUserInfoApi, action.userId)
        
       // 声明 effects 函数 put: 相当于 store中的dispatch
      put(updateUserInfoAction(res))
    }
    function* testSaga() {
       // 当action-type被准备dispatch时,执行 getUserInfo
       // 声明 effects 函数:takeEvery 监听一个action
        yield takeEvery('GET_USERINFO_ACTION', getUserInfoSaga)
    }
    
    export default testSaga
    

      

      这样就完成了一个简单的redux-saga的配置和使用,在component中dispatch getUserInfoAction这个action,就会执行 getUserInfoSaga 函数,这样就完成了异步的拓展。

      redux-saga中有很多 声明 effects 函数(比如:call、put、takeEvery、all、fock等等),具体请查阅redux-saga文档。

    备注:redux-saga函数必须是一个Generator函数

     

    拓展:还可以通过以下代码来将saga进行模块化:

    import { all, fork } from 'redux-saga/effects'
    // 以下saga是我个人项目中使用到的
    import headNavigationBarSagas from '@/commponents/HeadNavigationBar/store/sagas'
    import viewsHomeSagas from '@/views/Home/store/sagas'
    import viewsDetailSagas from '@/views/Detail/store/sagas'
    import viewsLoginSagas from '@/views/Login/store/sagas'
    import backstageArticleManage from '@/views/backstage/ArticleManage/store/sagas'
    
    // 整合多个模块的saga
    export default function * rootSaga () {
      yield all([
        fork(headNavigationBarSagas),
        fork(viewsHomeSagas),
        fork(viewsDetailSagas),
        fork(viewsLoginSagas),
        fork(backstageArticleManage)
      ])
    }
    

      

    大致介绍了redux、react-redux的基本用法和redux-saga中间件的使用,若有错误请各路大佬指出加以改正和学习

    ——
  • 相关阅读:
    flutter资料
    flutter兼论
    Dart 学习
    flutter简易教程
    全球15个顶级技术类博客
    Grunt压缩HTML和CSS
    用grunt搭建自动化的web前端开发环境-完整教程
    正确代码之-grunt
    grunt写一个px和rem互转的工具
    unslider使用方法1
  • 原文地址:https://www.cnblogs.com/jingxuan-li/p/12439181.html
Copyright © 2011-2022 走看看