React学习——Redux(十四)

1、redux-saga

① redux-saga异步解决方案

  1. 上面使用redux-thunk可以看到,异步的请求操作全被放在了action里面去执行,这样让其功能不够纯粹,所以需要用到redux-saga,它可以将异步操作单独提取出去,让action只需要声明方法即可,这样更易于维护,逻辑也更精确。首先安装:npm install redux-saga

  2. 这个案例我也是用到了上面的服务端的代码,用来进行异步操作,代码和上面案例的一样,只需要开启服务器即可。

  3. 来看一下reducer的代码:

    • 来看一下action方法:

      // src/Store/Actions/Person.action.js
      // 定义一个 action
      export const load_person = () => ({
        type: 'load_person'
      }) 
      
    • 下面就是saga相关的代码,在Store里面创建一个Saga文件夹,再在这个文件夹中创建一个Person.saga.js文件,用来编写对应的saga代码:

      // src/Store/Saga/Person.saga.js
      // 创建的 person 状态管理的 saga 中间件文件
      
      // 导入 axios
      import axios from 'axios'
      // 导入 redux-saga/effects 中的两个方法
      import { takeEvery, put } from 'redux-saga/effects'
      
      // 创建 loadPerson,进行异步操作
      function* loadPerson() {
        // 发送请求,获取用户数据
        const persons = yield axios.get('http://localhost:3005/api/getUsers')
                                  .then(response => response.data)
      
        // 请求成功后,调用 load_person_success,并将数据传递过去
        yield put({ type: 'load_person_success', payload: persons })
      }
      
      // 导出 PersonSaga 方法
      export default function* PersonSaga() {
        // 拦截在 action 中定义的 load_person 方法
        yield takeEvery('load_person', loadPerson)
      } 
      

      注意:

      1. redux-saga/effects中的takeEvery()方法,是用来进行拦截action事件的,其有两个参数:
        • 参数一:action的事件名
        • 参数二:自己创建的异步操作函数
      2. redux-saga/effects中的put()方法,只用了进行调用action方法进行传递数据的,其有两个参数:
        • 参数一:action的事件名
        • 参数二:要传递的数据
    • 来查看reducer里面的代码:

      // src/Store/Reducer/Person.reducer.js
      // 用户信息状态管理文件
      
      // 初始化一个默认的 state
      const defaultState = {
        person: []
      }
      
      // 创建 PersonReducer
      const PersonReducer = (state = defaultState, action) => {
        // 根据传递的方法名不同,进行不同的方法操作
        switch (action.type) {
          case 'load_person_success':
            return {
              person: action.payload
            }
          default:
            return state
        }
      }
      
      // 导出
      export default PersonReducer 
      
    • 合并reducer:

      // src/Store/Reducer/index.js
      // 合并 reducer
      // 导入 combineReducers 方法
      import { combineReducers } from "redux";
      
      // 导入 reducer
      import PersonReducer from './Person.reducer'
      
      // 导出合并的 reducer
      export default combineReducers({
        PersonReducer
      }) 
      
  4. 在主入口文件index.js中,导入redux-saga中的createSagaMiddleware()方法,使用saga创建中间件:

    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    
    // 导入 redux
    import { createStore, applyMiddleware } from 'redux'
    // 导入 react-redux 的 Provider 模块
    import { Provider } from 'react-redux'
    // 导入 redux-saga 中的 createSagaMiddleware 方法
    import createSagaMiddleware from 'redux-saga'
    
    // 导入 ContentReducer
    import totalReducer from './Store/Reducer'
    // 导入创建的 person saga
    import PersonSaga from './Store/Saga/Person.saga'
    
    // 创建一个 saga 中间件
    const sagaMiddleware = createSagaMiddleware()
    
    // 初始化 store
    const store = createStore(totalReducer, applyMiddleware(sagaMiddleware))
    
    // 注册自己定义的 saga 方法,必须要在初始化 store 之后
    sagaMiddleware.run(PersonSaga)
    
    ReactDOM.render(
      <React.StrictMode>
        {/* 使用 Provider 包裹 App 组件,并且传递 store 状态管理 */}
        <Provider store={ store }>
          <App />
        </Provider>
      </React.StrictMode>,
      document.getElementById('root')
    ); 
    
  5. 来看看主入口页面App.jsx的代码:

    // src/App.jsx
    // redux 工作流程梳理
    
    import { Component } from 'react';
    // 导入 connect 函数
    import { connect } from "react-redux";
    // 导入 bindActionCreators
    import { bindActionCreators } from "redux";
    // 导入 Person reducer 里面的方法
    import * as PersonActions from "./Store/Actions/Person.action";
    
    class App extends Component {
      handleClick = () =>{
        this.props.load_person()
      }
    
      render() {
        console.log(this.props);
        return (
          <div>
            <button onClick={ this.handleClick }>获取数据</button>
          </div>
        )
      }
    }
    
    // 获取 store 中的数据
    const mapStateToProps = (state) => {
      return {
        person: state.PersonReducer.person
      }
    }
    
    // 自动触发 action 的函数
    const mapActionsToProps = (dispatch) => {
      return {
        ...bindActionCreators(PersonActions, dispatch),
      }
    }
    
    export default connect(mapStateToProps, mapActionsToProps)(App); 
    
  6. 来看看页面效果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIQB6ZHv-1654949415634)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/abe2f37244e8471bae9494cc4476d0e4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image)]

② redux-saga拆分与合并

  1. saga的拆分与合并,与reducer的拆分与合并是一样的逻辑,防止以后saga越来越多,所以将其单独导入一个文件中,再整体导出。

  2. Store/Saga文件夹中创建一个root.saga.js文件,用来合并saga:

    // src/Store/Saga/root.saga.js
    // 合并 saga
    
    // 导入 redux-saga/effects 中的 all 方法,用来合并 saga
    import { all } from 'redux-saga/effects'
    
    // 导入 PersonSaga
    import PersonSaga from './Person.saga'
    
    // 导出
    export default function* RootSaga() {
      // 合并 saga
      yield all([
        // 因为 saga 是一个函数,所以需要调用
        PersonSaga()
      ])
    } 
    
  3. 在主入口文件index.js中进行导入合并的saga:

    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    
    // 导入 redux
    import { createStore, applyMiddleware } from 'redux'
    // 导入 react-redux 的 Provider 模块
    import { Provider } from 'react-redux'
    // 导入 redux-saga 中的 createSagaMiddleware 方法
    import createSagaMiddleware from 'redux-saga'
    
    // 导入 ContentReducer
    import totalReducer from './Store/Reducer'
    // 导入合并 saga
    import RootSaga from './Store/Saga/root.saga'
    
    // 创建一个 saga 中间件
    const sagaMiddleware = createSagaMiddleware()
    
    // 初始化 store
    const store = createStore(totalReducer, applyMiddleware(sagaMiddleware))
    
    // 注册合并的 saga,必须要在初始化 store 之后
    sagaMiddleware.run(RootSaga)
    
    ReactDOM.render(
      <React.StrictMode>
        {/* 使用 Provider 包裹 App 组件,并且传递 store 状态管理 */}
        <Provider store={ store }>
          <App />
        </Provider>
      </React.StrictMode>,
      document.getElementById('root')
    ); 
    

2、简化action与reducer

  1. 在之前代码的编写中,每次编写action都需要自己定义一个type方法类型,然后返回;定义reducer的时候也是每次需要通过switch进行判断,再去执行相应的方法。所以为了简化这两个步骤,用到一个库:npm install redux-actions

  2. 来看关于reducer的代码:

    • Counter装啊提管理action方法的代码:

      // src/Store/Actions/Counter.action.js
      // Counter 的 action 文件
      
      // 导入 redux-actions 里面的 createAction 方法
      import { createAction } from 'redux-actions'
      
      // +count 的函数
      export const increment = createAction('increment')
      
      // -count 的函数
      export const decrement = createAction('decrement') 
      

      注意: 导入redux-actions中的createAction()方法,这个方法是用来进行创建action方法的,参数就是要创建的action的方法名

    • Counter状态管理reducer的代码:

      // src/Store/Reducer/Counter.reducer.js
      // 自定义的一个 count 的 reducer
      
      // 导入 redux-actions 中的 handleActions 方法
      import { handleActions as createReducer } from 'redux-actions'
      // 导入 action 中定义的两个方法
      import { increment, decrement } from '../Actions/Counter.action'
      
      // 初始化一个默认的 state
      const defaultState = {
        count: 0
      }
      
      // 使用 handleActions 方法创建 reducer
      const CounterReducer = createReducer({
        // increment 事件,count + 1
        [increment]: (state, action) => ({ count: state.count + 1 }),
        // decrement 事件,count - 1
        [decrement]: (state, action) => ({ count: state.count - 1 })
      }, defaultState)
      
      // 导出
      export default CounterReducer 
      

      注意:

      1. 导入redux-actions中的handleActions()方法,为了让其语义化更明显,所以我进行了起别名操作:handleActions as createReducer
      2. handleActions()方法有两个参数:
        • 参数一:定义的事件对象,每一个属性就是action文件中导出的方法,注意一定要用 [] 进行包裹。
        • 参数二:定义的state数据。
    • 合并reducer:

      // src/Store/Reducer/index.js
      // 合并 reducer
      // 导入 combineReducers 方法
      import { combineReducers } from "redux";
      
      // 导入 reducer
      import CounterReducer from './Counter.reducer'
      
      // 导出合并的 reducer
      export default combineReducers({
        CounterReducer
      }) 
      
  3. 在主入口文件导入合并的reducer,进行注册store:

    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    
    // 导入 redux
    import { createStore } from 'redux'
    // 导入 react-redux 的 Provider 模块
    import { Provider } from 'react-redux'
    
    // 导入 ContentReducer
    import totalReducer from './Store/Reducer'
    
    // 初始化 store
    const store = createStore(totalReducer)
    
    ReactDOM.render(
      <React.StrictMode>
        {/* 使用 Provider 包裹 App 组件,并且传递 store 状态管理 */}
        <Provider store={ store }>
          <App />
        </Provider>
      </React.StrictMode>,
      document.getElementById('root')
    ); 
    
  4. 来看一下主入口页面App.jsx文件中的代码:

    // src/App.jsx
    // redux 工作流程梳理
    
    import { Component } from 'react';
    // 导入 connect 函数
    import { connect } from "react-redux";
    // 导入 bindActionCreators
    import { bindActionCreators } from "redux";
    // 导入 Counter reducer 里面的方法
    import * as CounterActions from "./Store/Actions/Counter.action";
    
    class App extends Component {
    
    
      render() {
        console.log(this.props);
        return (
          <div>
            <button onClick={ this.props.decrement }>-1</button>
            <span> { this.props.count } </span>
            <button onClick={ this.props.increment }>+1</button>
          </div>
        )
      }
    }
    
    // 获取 store 中的数据
    const mapStateToProps = (state) => ({
      count: state.CounterReducer.count
    })
    
    // 自动触发 action 的函数
    const mapActionsToProps = (dispatch) => ({
      ...bindActionCreators(CounterActions, dispatch)
    })
    
    export default connect(mapStateToProps, mapActionsToProps)(App); 
    
  5. 来看看页面效果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cbonKD8-1654949415635)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b75323f838c04656a54ef97e3ac5030a~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image)]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React中父组件向子组件传值可以通过props实现,而子组件向父组件传值可以通过回调函数实现。 假设我们有一个父组件Modal,其中包含一个子组件Form,我们希望在Form表单中填写完数据后,将数据传递给Modal组件进行处理。 首先,我们在Modal组件中定义一个state,用来保存Form表单中的数据: ```javascript class Modal extends React.Component { constructor(props) { super(props); this.state = { formData: {} }; } // ... } ``` 然后,在Modal组件中定义一个函数,用来接收Form组件传递的数据,并更新Modal组件的state: ```javascript handleFormData = (data) => { this.setState({ formData: data }); } ``` 接下来,在render函数中,将handleFormData函数传递给Form组件作为props: ```javascript render() { return ( <div> <Form onFormData={this.handleFormData} /> </div> ); } ``` 在Form组件中,我们通过props接收父组件传递过来的onFormData函数,并在表单提交时调用该函数将数据传递给父组件: ```javascript class Form extends React.Component { handleSubmit = (event) => { event.preventDefault(); const data = { name: event.target.name.value, age: event.target.age.value }; this.props.onFormData(data); } render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" name="name" placeholder="姓名" /> <input type="text" name="age" placeholder="年龄" /> <button type="submit">提交</button> </form> ); } } ``` 注意,这里我们使用了event.target来获取表单中的数据,而不是使用refs或者state来获取数据,这是因为React不推荐直接操作DOM元素。 最后,当Form表单提交后,父组件的state中就会保存表单中的数据,我们可以在Modal组件中对数据进行处理或者展示。 这就是父子组件之间传值的基本方法,通过props和回调函数,可以轻松地实现组件之间的数据传递。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值