redux与redux-thunk的使用

person smartzeng    watch_later 2018-05-20 22:44:38
visibility 687    class redux,thunk,redux-thunk,react    bookmark 分享

redux

  • redux是一个可预测的状态容器,(区别于Wordpress framework - Redux Framework)
  • 它可以帮助您编写在不同环境(客户端,服务器和本地)中运行一致并且易于测试的应用程序。 最重要的是,它提供了出色的开发者体验,例如结合时间旅行调试器进行实时代码编辑。
  • 你可以结合react一起使用redux。或者其他任何的视图库,redux很小,包括依赖仅仅2kb。
  • 安装
npm install --save redux
# 补充包
# 也许你还要和react一起使用,并且需要开发工具
npm install --save react-redux
npm install --save-dev redux-devtools
  • 例子
import { createStore } from 'redux'
 
/**
* 这是一个reducer,它是一个具有(state,action)=>状态签名的纯函数。
* 它描述了动作如何将状态转换为下一个状态。
* 状态的形状取决于你:它可以是一个基元,一个数组,一个对象,
* 甚至是Immutable.js数据结构。 唯一重要的部分是你应该
* 不改变状态对象,但如果状态改变则返回一个新的对象。
* 在这个例子中,我们使用`switch`语句和字符串,但是你可以使用一个帮助器
* 遵循不同的约定(如功能图),如果它适合你的项目。
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1
  case 'DECREMENT':
    return state - 1
  default:
    return state
  }
}
 
// 创建一个持有应用状态的Redux store。
// 他的api { subscribe, dispatch, getState }.
let store = createStore(counter)
 
// 你可以通过subscribe()来更新ui来响应状态的改变。
// 通常你会使用视图绑定库 (比如:React-Redux) 比起直接用 subscribe() 。
// 但是,将当前状态保存在localStorage中也可能非常方便。
 
store.subscribe(() =>
  console.log(store.getState())
)
 
// 改变内部状态的唯一方法是发送一个action。
// 这些动作可以被序列化,记录或存储,并在以后重播。
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })

您不需要直接改变状态,而是使用称为操作的简单对象指定想要发生的突变。然后编写一个称为reducer的特殊函数来决定每个操作如何转换整个应用程序的状态。

如果您用过Flux,那么您需要了解一个重要的差异。 Redux没有分派器或支持许多store。相反,只有一个store具有单一根reducer函数。随着您的项目的扩展,您不必添加store,而是将根reducer分解为更小的reducer,并独立运行于状态树的不同部分。这就好像React应用程序中只有一个根组件,但它由许多小组件组成。

这种体系结构可能看起来像一个反应器应用程序的矫枉过正,但这种模式的美妙之处在于它如何适应大型复杂的应用程序。它还支持非常强大的开发者工具,因为可以追踪引发它的操作的每个变异。您可以记录用户会话并通过重播每一个动作来重现它们。

中间件redux-thunk

  • 用途
    redux thunk 中间件允许你写入一个action创建者通过返回一个函数来代替action。thunk通常被用于延迟一个action的分发。或分发一些条件可以预测的action。内部函数接收store方法dispatch和getState作为参数。一个thunk函数包含一个表达式来延迟他的计算。
    - 安装
npm install --save redux-thunk
  • 启用redux-thunk,使用applyMiddleware()
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
 
// Note: redux@>=3.1.0
const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);
  • 例子如下:
const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
 
function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}
// 异步 
function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // 用dispatch可以处理同步和异步的action
      dispatch(increment());
    }, 1000);
  };
}
// 一个action创建者返回一个函数去实现条件分发
function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();
 
    if (counter % 2 === 0) {
      return;
    }
 
    dispatch(increment());
  };
}
  • 作用
    任何来自内部函数的返回值都将作为dispatch本身的返回值。 这对于编写一个异步控制流来说非常方便,因为thunk动作创建者相互调度并返回Promise以等待对方的完成:
  • 示例代码
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
 
// Note: redux@>=3.1.0
const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);
 
function fetchSecretSauce() {
  return fetch('https://www.google.com/search?q=secret+sauce');
}
 
// 正常action的创建
// 他们返回的action可以在没有任何中间件的情况下派发
// 然而他们只能表达事实,而不是异步流程。
 
function makeASandwich(forPerson, secretSauce) {
  return {
    type: 'MAKE_SANDWICH',
    forPerson,
    secretSauce
  };
}
 
function apologize(fromPerson, toPerson, error) {
  return {
    type: 'APOLOGIZE',
    fromPerson,
    toPerson,
    error
  };
}
 
function withdrawMoney(amount) {
  return {
    type: 'WITHDRAW',
    amount
  };
}
 
// 甚至没有中间件你可以分发action.
store.dispatch(withdrawMoney(100));
 
// 当你需要使用异步操作比如api的调用或路由转换
// thunk函数返回一个函数
function makeASandwichWithSecretSauce(forPerson) {
 
  // 反转控制
  // 返回一个接受`dispatch`的函数,以便稍后分发。
  // Thunk 中间件知道如何将异步action转换为action
  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}
 
// Thunk 中间件让我们分发thunk异步action
// 就好像自己的action一样
store.dispatch(
  makeASandwichWithSecretSauce('Me')
);
 
// 它甚至需要关心thunk的返回值
// 对于dispatch, 我们只有声明一个promise对象并返回他们.
 
store.dispatch(
  makeASandwichWithSecretSauce('My wife')
).then(() => {
  console.log('Done!');
});
 
// 事实上,我们通过编写dispacth的action。
// 来自其他action创建者创建的action和 异步actiuon,
// 我们可以通过promise构建控制流
 
function makeSandwichesForEverybody() {
  return function (dispatch, getState) {
    if (!getState().sandwiches.isShopOpen) {
 
      // 你不必返回promise对象,这是很便利的
      // 因此调用者可以始终使用.then()函数来获取异步分发的结果
      return Promise.resolve();
    }
 
    // 我们可以分发普通对象action和其他thunk,
    // 这使得我们可以在单个流程中组合异步操作
 
    return dispatch(
      makeASandwichWithSecretSauce('My Grandma')
    ).then(() =>
      Promise.all([
        dispatch(makeASandwichWithSecretSauce('Me')),
        dispatch(makeASandwichWithSecretSauce('My wife'))
      ])
    ).then(() =>
      dispatch(makeASandwichWithSecretSauce('Our kids'))
    ).then(() =>
      dispatch(getState().myMoney > 42 ?
        withdrawMoney(42) :
        apologize('Me', 'The Sandwich Shop')
      )
    );
  };
}
 
// 这对服务端渲染非常有用,因为我们可以等待服务器的响应。
// 知道服务端响应的数据完成然后才开始同步渲染应用。
store.dispatch(
  makeSandwichesForEverybody()
).then(() =>
  response.send(ReactDOMServer.renderToString())
);
 
//我们也可以在一个组件中分发一个异步的thunk action。
// 任何时间它的props的改变将加载缺少的数据
 
import { connect } from 'react-redux';
import { Component } from 'react';
 
class SandwichShop extends Component {
  componentDidMount() {
    this.props.dispatch(
      makeASandwichWithSecretSauce(this.props.forPerson)
    );
  }
 
  componentWillReceiveProps(nextProps) {
    if (nextProps.forPerson !== this.props.forPerson) {
      this.props.dispatch(
        makeASandwichWithSecretSauce(nextProps.forPerson)
      );
    }
  }
 
  render() {
    return 

{this.props.sandwiches.join('mustard')}

} } export default connect( state => ({ sandwiches: state.sandwiches }) )(SandwichShop);
  • 注入自定义参数
    自2.1.0以来,Redux Thunk支持使用withExtraArgument函数注入自定义参数:
const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument(api))
)
 
// 然后
function fetchUser(id) {
  return (dispatch, getState, api) => {
    // 你可以在这里使用api
  }
}
  • 要传递多个东西,只需将它们包装在单个对象中并使用解构:
const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument({ api, whatever }))
)
 
// 然后
function fetchUser(id) {
  return (dispatch, getState, { api, whatever }) => {
    // 你可以在这里使用api和其他东西
  }
}
评论区
评论列表
menu