redux&&react-redux&&redux-thunk

react redux react-redux redux-thunk

react --> UI
redux --> 存储数据(可以理解为一个数据库)
react-redux --> redux搭配react使用的库(redux写起来比较麻烦,react-redux封装好一些方法便于开发)
redux-thunk --> 中间件,可以dispatch异步请求

redux

redux中的reducer为什么是纯函数?

不考虑性能的话,写不纯的reducer来变动数据在技术上是可行的,但并不鼓励这么做。不纯的reducer会使得一些开发特性,如时间旅行、记录/回访或热加载不可实现。此外,这种数据不可变动的特性, 不会产生性能问题,即使对象分配失败,仍然可以防止昂贵的冲渲染和计算,这得益于reducer的纯度,应用内的变化更是一目了然

使用tips

  1. 应该尽量减少在action中传递的数据
  2. 保持reducer的纯净,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
  3. 在reducer中,不要修改state(时刻谨记永远不要在克隆 state 前修改它)。使用 Object.assign() 新建了一个副本。不能这样使用 Object.assign(state, { visibilityFilter: action.filter }),因为它会改变第一个参数的值。你必须把第一个参数设置为空对象。你也可以开启对ES7提案对象展开运算符的支持, 从而使用 { …state, …newState } 达到相同的目的
function todoApp(state = initialState, action) {
    
    
  switch (action.type) {
    
    
    case SET_VISIBILITY_FILTER:
      return Object.assign({
    
    }, state, {
    
    
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}
  1. createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
    完整参数:createStore(reducer, defaultState, applyMiddleware(thunk))
    一般使用形式:createStore(reducer, applyMiddleware(thunk))
let store = createStore(todoApp, window.STATE_FROM_SERVER)

redux写法

import {
    
     createStore, combineReducers } from 'redux';
const initState = {
    
     count: 1 };

// reducer
const counter = function( state = initState, action ){
    
    
  let count = state.count  // 1
  switch(action.type){
    
    
    case 'INCREMENT':
      count++;
      return {
    
     count };
    case 'DECREMENT':
      count--;
      return {
    
     count };
    default:
      return state
  }
}

// reducer组合
const reducers = combineReducers({
    
     counter: counter });

// 创建store
let store = createStore(reducers)

export default store

// 监听数据变化,返回注销监听的方法
const unsubscribe = store.subscibe(() => {
    
    
  // 获取state
  console.log(store.getState())  // { counter: { count: 1 } }  格式:{ ReducersName: { 对应的state } }
})

// 派发action 修改state
store.dispatch({
    
     type: 'INCREMENT', payload: {
    
     count: 10 } });

react-redux

tips

  1. 容器组件和展示组件相分离
    a. 容器组件用来请求数据(描述如何运行(数据获取、状态更新)),展示组件注重UI展示(描述如何展现(骨架、样式)),这样一来就使得展示组件可以复用
    b. 技术上讲,容器组件就是使用store.subscribe()从Redux state树中读取部分数据,并通过props来把这些数据提供给要渲染的组件。但建议使用react-redux库的connect()方法来生成,这个方法做了性能优化来避免很多不必要的重复渲染
  2. 建议使用指定的 React Redux 组件 来让所有容器组件都可以访问 store,而不必显示地传递它。只需要在渲染根组件时使用即可
// index.js
import React from 'react'
import {
    
     render } from 'react-dom'
import {
    
     Provider } from 'react-redux'
import {
    
     createStore } from 'redux'
import App from './components/App'

let store = createStore(todoApp)
render(
  <Provider store={
    
    store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
  1. 根路径("/")传参
//这使得可以在 App 中获取 params 的属性。params 是一个包含 url 中所有指定参数的对象。 例如:如果我们访问 localhost:3000/completed,那么 params 将等价于 { filter: 'completed' }。
<Route path="/(:filter)" component={
    
    App} />
// App.js
const App = ({
    
     params }) => {
    
    
  return (
    <div>
      <AddTodo />
      <VisibleTodoList
        filter={
    
    params.filter || 'all'}
      />
      <Footer />
    </div>
  );
};

react-redux使用

import {
    
     connect } from 'react-redux';

function App(props){
    
    
  const {
    
     count, add, sub } = props;
  
  return (
    <div className="App">
      <button onClick={
    
    add}>1</button>
      <hr></hr>
      <button onClick={
    
    sub}>1</button>
      <hr></hr>
      <span>{
    
    count}</span>
    </div>
  )
}

const mapStateToProps = (state) => {
    
    
  let count = state.counter.count
  return {
    
    
    count
  }
}

const mapDispatchToProps = dispatch => ({
    
    
  add: () => {
    
    
    dispatch({
    
     type: 'INCREMENT' });
  },
  sub: () => {
    
    
    dispatch({
    
     type: 'DECREMENT' });
  },
})

export default connect(mapStateToProps, mapDispatchToProps)(App)

redux异步解决方案

  • Redux本身只能处理同步的Action,但可以通过中间件来拦截处理其它类型的action,比如函数(Thunk),再用回调触发普通Action,从而实现异步处理,在这点上所有Redux的异步方案都是类似的。
  • 使用redux-thunk中间件
    (本质:通过使用指定的 middleware,action 创建函数除了返回 action 对象外还可以返回函数。这时,这个 action 创建函数就成为了 thunk–原先的action是对象({type:’’,payload:{}})的形式,加入中间件之后,可以将action写为函数形式,也就是说可以dispatch一个带有副作用的函数)
//  thunk 的一个优点是它的结果可以再次被 dispatch
import thunkMiddleware from 'redux-thunk';
import {
    
     createLogger } from 'redux-logger';

const loggerMiddleware = createLogger();

// 使用thunk中间件(这样一来就可以dispatch带有副作用的函数了)
let store = createStore(reduces, applyMiddleware(thunkMiddleware,loggerMiddleware))

// connect进component的函数
const mapDispatchToProps = (dispatch) => {
    
    
  return {
    
    
    add: () => {
    
    
      dispatch({
    
     type: 'INCREMENT' });
    },
    sub: () => {
    
    
      dispatch({
    
     type: 'DECREMENT' });
    },
    getMenu: () => {
    
     // 使用thunk
      dispatch((dispatch) => {
    
     // dispatch函数
        axios.get('xxxxx').then((res) => {
    
    
          dispatch({
    
     // 这里可以二次dispatch
            type: 'DECREMENT',
          });
        });
      });
    },
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(App);
  • Thunk middleware 并不是 Redux 处理异步 action 的唯一方式:
    可以使用 redux-promise 或者 redux-promise-middleware 来 dispatch Promise 来替代函数。
    可以使用 redux-observable 来 dispatch Observable。
    可以使用 redux-saga 中间件来创建更加复杂的异步 action。
    可以使用 redux-pack 中间件 dispatch 基于 Promise 的异步 Action。

  • Monkeypatch

    // 实现每次更新store打印日志的效果
    // 直接修改 store 实例中的 dispatch 函数
    let next = store.dispatch
    store.dispatch = function dispatchAndLog(action) {
          
          
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
    

猜你喜欢

转载自blog.csdn.net/qq_36303110/article/details/113886831