日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

由redux到react-redux再到rematch

作者:IsPinocchio 更新時間: 2021-12-11 編程語言

這比較適合看過redux但看了跟沒看一樣的同學,幫助記憶理清里面的基礎知識

一. Redux

1. Store

Store是保存數據的地方,可以把它看做一個容器。整個應用只能有一個Store。

Redux提供createStore這個函數生成store

import { createStore } from 'redux';
const store = createStore(fn);

// store也可以接受第二個參數:state的初始值,他會覆蓋reducer的state參數中的默認初始值
const store = createStore(fn, initState);

2. State

當你想拿到某個時刻的數據state時,需要生成對應的store快照。

const state = store.getState();

3. Action

由于用戶觸碰不到代碼(State),只能通過觸碰View來通知代碼服務(store)改變State,這個由View發起的通知就是Action。

Action是一個對象,其中type是必須的,表示Action的名稱。其他屬性可以自由設置。

const action = {
  type: "ADD",
  payload: "data"
}

可以理解為,要觸發一個ADD行為,攜帶的參數是payload

3.3 Action Creator

類似于工廠模式,Action肯定會有很多種消息,因此可以寫一個Action Creator來生產一個Action

function AddCreator(text) {
  return {
    type: "ADD",
    payload: text
  }
}

4. Dispatch

你可以將action理解為一個通知單,那么View如何發出這個通知單給代碼服務(store)呢,就是通過dispatch

只要給dispatch傳入一個action參數,View就把通知單發出去了!

import { createStore } from 'redux';
const store = createStore(fn);

const action = {
  type: "ADD",
  payload: "data"
}

store.dispatch(action);

5.Reducer

View把通知單傳給代碼服務后,代碼服務(Store)總要根據action中的內容去進行相應的處理的。reducer就是用來識別action并進行相應處理的處理器。他接收當前State和Action作為參數,返回一個新的State。

//其實就是先去判斷action要執行哪個操作,再把對應的state更新并返回
function reducer = (state = initState, action) => {
  switch(action.type) {
    case 'ADD':
      return state + action.payload;
    default:
      return state
  }
}

注意這個處理器是store的,也就是說,store的創建是伴隨reducer參數來初始化的。

import { createStore } from 'redux';
const store = createStore(reducer);

6. Subscribe

Store也提供了subscribe方法來設置監聽函數,一旦State發生變化,就會自動執行這個函數。

store.subscribe返回一個函數,調用這個函數可以解除監聽。

const listener = function() {
	console.log(store.getState())
}
let unsubscribe = store.subscribe(listener);

// 解除監聽
unsubscribe();

過度------------簡易使用

你知道的,畢竟store不是react組件中的state,store中state的更新并不會讓View也同時變化,你需要手動調用render重新渲染。

const Counter = ({ value, onIncrement, onDecrement }) => (
  <div>
  <h1>{value}</h1>
  <button onClick={onIncrement}>+</button>
  <button onClick={onDecrement}>-</button>
  </div>
);

const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
};

const store = createStore(reducer);

const render = () => {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => store.dispatch({type: 'INCREMENT'})}
      onDecrement={() => store.dispatch({type: 'DECREMENT'})}
    />,
    document.getElementById('root')
  );
};

render();
// 通過監聽state的改變來更新視圖
store.subscribe(render);

7. 中間件

當我們想要在store的整個流程中添加一些其他功能時,我們該往哪里添加?

處理流程中涉及邏輯進程的主要就是dispatch和reducer了,但reducer要求是純函數,不能涉及副作用,那只能在dispatch中做手腳了。

先看一下想添加一個日志功能,并將更新后的state打印出來該如何實現:

let next = store.dispatch;
store.dispatch = function changeDispatch(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

上面對store.dispatch進行了改造,添加了一些功能,這就是中間件的雛形。中間件其實就是一個函數。

7.1 applyMiddlewares()

Redux提供了原生方法applyMiddlewares來加入中間件,增強store.dispatch的功能。

他可以將中間件組成一個數組,然后依次執行。

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(logger, ...otherMiddlewares)
);

8. 如何處理異步操作

Action發出后,過一段時間再執行Reducer,這就是異步。

那么Reducer作為純函數肯定是不可以在這里添加處理異步邏輯的,那么就得在store.dispatch中做手腳了。

想一想,如果不能同步的進行dispatch(異步) -> reducer,那不如將dispatch分為兩步走,dispatch(異步獲取) -> dispatch(同步發送action) -> reducer

// 操作發起時的 Action
// 操作成功時的 Action
// 操作失敗時的 Action

具體如何改造多次的dispatch可以使用redux-thunk或者redux-promise,他們處理后,dispatch可以接受不同的參數,不再局限于action對象。

二. React-Redux

回憶下之前的Redux,state更新時我們需要設置監聽函數實時re-render才能更新View,想要在其他組件中使用store都得引入一下。

React-Redux解決了上述的痛點:

  • 提供Provider把state注入全局,這樣子組件就不用單獨引入了
  • 響應式。無需我們手動監聽,state的更新會自動幫我們更新View
  • 通過connnct把state和dispatch注入到當前組件的props上

1. Provider

React-Redux提供了組件,使得整個app能夠訪問到store中的數據,Provider是用context封裝的。

2. connect

React-Redux將組件分為兩種,一種是UI組件,另一種是容器組件。

UI組件只通過接受props來顯示UI,容器組件負責管理數據和業務邏輯(也就是承接state和dispatch)

React-Redux提供一個connect方法能夠把組件和store連接起來,把state、dispatch方法都放入到當前組件上,同時是state變成響應式。其實就是一個高階組件包裝了一下我們寫的UI組件。

// index.js
import React from 'react';
import ReactDom from "react-dom"
//引入Provider
import {Provider} from "react-redux";

import Counter from "./counter/ReactReduxCounter";

import store from "./redux/basic/store"

ReactDom.render(
    <Provider store={store}>
        <Counter></Counter>
    </Provider>,
    document.getElementById("root")   
)

我們看到通過用<Provider/>來包裝我們真正的reactDOM,就可以讓里面所有的子組件都使用store了。

注意:用到store的子組件應該是通過connect包裝后的。

// Counter.js
import React, { Component } from 'react';
import {connect} from "react-redux"

//子組件
class Counter extends Component {
    //方法調用后自動更新數據
    increment=()=>{
        this.props.increment()
    }
    decrement=()=>{
        this.props.decrement()
    }
    render() {
        return (
            <div>
                <div>{this.props.num}</div>
                
                <button onClick={this.increment}>點我+1</button>
                <button onClick={this.decrement}>點我-1</button>
            </div>
        );
    }
}

//該函數作為connect的第一個參數,能拿到state
//映射state到組建的props上
function mapStateToProps(state){
    return {
        num:state.counter
    }
}

//該函數作為connect的第二個參數,能拿到dispatch
//映射dispatch方法到組建的props上
function mapDispatchToProps(dispatch){
    return {
        increment(){
            dispatch({
                type:"increment"
            })
        },
        decrement(){
            dispatch({
                type:"decrement"
            })
        }
    }
}

//connet函數執行返回一個高階組件
//調用這個高階組件,傳入當前組件Counter作為參數,返回一個增強的Counter組件
//這個增強的組件props里有store的state和dispach方法
export default connect( mapStateToProps , mapDispatchToProps )(Counter)
/// store.js
import { createStore } from "redux"

const defaultState={
    counter:0
}

//純函數
let reducers =(state = defaultState ,action)=>{
    switch (action.type){
        case "increment":
            console.log("increment")
            return {
                counter:state.counter+1
            }
        case "decrement":
            return {
                counter:state.counter-1
            }
        default :
        return state 
    }
}
const store = createStore(reducers)
export default store

3. mapStateToProps()和mapDispatchToProps()

注意到,上述connect()中傳遞了兩個參數:mapStateToProps()和mapDispatchToProps()。

通過觀察connect源碼(下一節)發現,他是利用了context封裝了一個高階組件,在封裝過程中,會調用上述兩個方法,把store中的state和dispatch取出來放到子組件的props里。這樣我們在編寫組件的時候,就可以無感知的調用state和dispatch了。

  • mapStateToProps

    • 他是一個函數,接受state作為參數,返回一個對象,這個對象是你想從state中取出的數據

    • const mapStateToProps = (state) => {
        return {
          counter: state.counter
        }
      }
      
  • maoDispatchToProps

    • 他是一個函數,接受dispatch和ownProps(容器組件的props)兩個對象,返回一個對象,這個對象中的值應該是一個包含可以觸發dispatch的方法。

    • const mapDispatchToProps = (dispatch, ownProps) => {
        return {
          increment(num) {
      			dispatch({
              type: "increment",
              payload: num
            })
          }
        }
      }
      

4. connect高階組件的大致實現

function connect(mapStateToProps, mapDispatchToProps) {
  return function(WrappedComponent) {
    class Connect extends React.Component {
      componentDidMount() {
        // 組件加載完成后訂閱store變化,如果store有變化則更新UI
        this.unsubscribe = this.context.store.subscribe(this.handleStoreChange.bind(this));
      }
      componentWillUnmount() {
        // 組件銷毀后,取消訂閱事件
        this.unsubscribe();
      }
      handleStoreChange() {
        // 更新UI
        this.forceUpdate();
      }
      render() {
        return (
          <WrappedComponent
            {...this.props}
            {...mapStateToProps(this.context.store.getState())} // 參數是store里面的數據
            {...mapDispatchToProps(this.context.store.dispatch)} // 參數是store.dispatch
          />
        );
      }
    }
    Connect.contextTypes = {
      store: PropTypes.object
    };
    return Connect;
  };
}

5. React-Redux的hook版

可能我們覺得每次都要用connect封裝一下組件太麻煩了,還要寫mapStateToProps和mapDispatchToProps,react-redux為我們提供了一些hooks,比如常用的useSelector和useDispatch。

  • useSelector接收兩個參數:selector和equalityFn。第一個參數是一個函數以獲取你想要的state,第二個可選參數也是個函數,用于比較狀態是否發生改變,如果函數執行結果為false就表示狀態發生改變,組件將重新渲染。
  • useDispatch直接調用可獲得dispatch
import { useSelector, useDispatch } from 'react-redux'

const Home = (props) => {
  const dispatch = useDispatch();
  const number = useSelector(state => state.number);
  
  const handleClick = () => {
    dispatch({
      type: 'ADD',
      payload: 1
    })
  }
  
  return (
  	<h2>{number}</h2>
    <button onClick={handleClick}/>
  )
}

6. Redux和React-Redux的關系

依初初初學者短見,我們想要在React項目中使用共享數據時,使用的store都是通過Redux提供的createStore創建的store。React-Redux只是幫我們封裝了一些方法,以幫助我們在子組件中更便利的使用state和dispatch等。

三. Rematch

如果說React-Redux是在Redux的基礎上幫助子組件更便捷的使用state和dispatch。

那Rematch則是在和React-Redux搭配基礎上幫我們更具封裝性的創建store,加上更便利的使用state和dispatch。

1. 創建store

之前我們是通過redux的createStore的方法來創建store。

import { createStore } from 'redux';
const store = createStore(reducer, initState);

如果還想要添加一些異步處理,那可能還要加入中間件,reducer也變得很復雜。

而Rematch提供了init可以讓我們把initState、reducers、異步處理方法封裝在一起。

// store.js
import { init } from '@rematch/core';

const count = {
  state: 0,
  reducers: {
    increment: (state, payload) => state + payload,
    decrement: (state, payload) => state - payload
  },
  effects: {  // 帶有異步處理數據的reducer
    async incrementAsync(payload) {
      await func();
      this.increment(payload);
    }
  }
}

export const store = init(count);

如果到此為止,你可以用上述生成的store正常的配合react-redux的provider、connect去使用他。

2. 模塊化

rematch建議我們將數據分類保存到models文件夾下的不同的文件里。最后在models/index.js中統一導出。

例如:

  • models/count.js
  • Models/user.js
// models/index.js
export { default as count } from './count'
export { default as user } from './user'
// store.js
import { init } from '@rematch/core'
import * as models from './models'

export const store = init({models})

于是我們在每個組件上,可以直接引入store來取到想要的某個模塊中的store和dispatch

// counter.js
import { store } from './store.js'

const counter = () => {
  const { counter, user } = store.getState();
  const add = store.dispatch.counter.add;
  const changName = store.dispatch.user.changeName;
	
  const handleClick = () => {
    add({
      type: 'ADD',
      payload: 1
    })
  }
  
  return (
  	<h2>{counter.number}</h2>
    <h2>{user.name}</h2>
    <button onClick={handleClick}/>
  )
}

但注意,以上state不是響應式的,也就是說觸發dispatch改變state并不會讓視圖更新,我們依然需要結合react-redux中的connect或者hook來讓其變成響應式。

原文鏈接:https://blog.csdn.net/Pinoochio/article/details/122115950

欄目分類
最近更新