React + Redux订阅mapStateToProps

时间:2017-05-08 22:52:57

标签: reactjs redux

我有mapStateToProps工作流程,但如果我想以不适合状态的方式响应操作=>道具范式?例如:

this.props.dispatch(saveArticle(...))
// on successful save, redirect to article page

如果我只是使用常规的旧XHR而不是动作,它看起来像这样:

saveArticle(...).then(article => this.router.push(`/articles/${article.id}`))

目前尚不清楚这将如何适应标准的React / Redux工作流程;我见过有人建议saveArticle()动作创建者可以启动路由器更改,但我想保持这些分开;我应该能够在不被强制重定向的情况下保存文章。

解决方法可能是在 mapStateToProps中执行;让动作设置一个标志或类似的东西,比如articleWasSaved,并让组件为该支柱执行保存查找并重定向(如果它看到它),但这看起来真的很难看,特别是如果多个事情正在寻找更新,因为它可能需要组件清除标志。

我缺少一个简单/标准的解决方案吗?

2 个答案:

答案 0 :(得分:2)

Redux-thunk 允许您将函数分派为操作。理想情况下,调度异步操作。

我在这里创建了一个例子,我觉得它对你有用:

actions.js

export const tryingAsyncAction = () => {
  return {
    type: 'TRYING_ASYNC_ACTION'
  }
}

export const actionCompleted = () => {
  return {
    type: 'ACTION_COMPLETED'
  }
}

export const errorAsyncAction = (error) => {
  return {
    type: 'ERROR_ASYNC_ACTION',
    error
  }
}

export function someAsynAction() {
  return dispatch => {
      dispatch(tryingAsyncAction())
      ApiService.callToAsyncApi(...)
        .then(() => {
          dispatch(actionCompleted())
        }, (cause) => {
          dispatch(errorAsyncAction(cause))
        })
  }
}

reducer.js

const initialState = {
  tryingAction: false,
  actionCompleted: false,
  error: null,
  shouldRedirect: false,
  redirectUrl: null
}

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case 'TRYING_ASYNC_ACTION':
      return Object.assign({}, state, {
        tryingAction: true
      })
    case 'ACTION_COMPLETED':
      return Object.assign({}, state, {
        tryingAction: false,
        actionCompleted: true,
        shouldRedirect: true,
        redirectUrl: 'someUrl'
      })
    case 'ERROR_ASYNC_ACTION':
      return Object.assign({}, state, {
        tryingAction: false,
        actionCompleted: false,
        error: action.error
      })
    default:
      return state
  }
}

您的createStore文件

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk' //npm install --save redux-thunk
//Other imports...

const store = createStore(
  reducer,
  applyMiddleware(
    thunkMiddleware
  )
)

YourComponent.js

componentWillReceiveProps(nextProps){
  if(nextProps.shouldRedirect && nextProps.redirectUrl)
    this.router.push(`/articles/${article.id}`)
}

如果有什么你不明白,请告诉我。我会尽力澄清

答案 1 :(得分:1)

在这种情况下,您可以使用react-thunk

动作/ index.js

export function saveArticle(data) {
  return (dispatch, getState) => (
    api.post(data).then(response => {
      dispatch({ type: 'SAVE_ARTICLE', payload: response })
      return response;
    })
  )
}

减速器/ index.js

import { combineReducers } from 'redux'; 

const initialState = { 
  list: [],
  current: null,
  shouldRedirect: false, 
  redirectTo: null
};

export function articles(state = initialState, action) {
  switch(action.type) {
    case 'SAVE_ARTICLE': 
      return {
        shouldRedirect: true,
        redirectTo: '/some/url',
        current: action.payload,
        list: [...state.list, action.payload]
      };
    default: return state;
  }
}

export default combineReducers({ articles });

存储/ index.js

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

// Note: this API requires redux@>=3.1.0
const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

组件/ index.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as Actions from 'actions/index';

class MyComponent extends Component {
  _handleSubmit = () => {
    // get form values somehow...
    // const values = getFormValues();

   this.props.saveArticle(values).then(response => {
     // you can handle you redirect here as well,
     // since saveArticle is returning a promise
   });
  };

  componentWillReceiveProps(nextProps) {
    // you can handle the redirection here listening to changes
    // on shouldRedirect and redirectTo that will be triggered
    // when the action 'SAVE_ARTICLE' is dispatched
    if(nextProps.shouldRedirect && nextProps.redirectTo) {
      this.routes.push(nextProps.redirectTo);
    }
  }

  render() {
    // just an example
    return (
      <form onSubmit={this._handleSubmit}>
        { /* ... other elements here */ }
      </form>
    )
  }
}

export default connect(
  state => ({ 
    articles: state.articles.list,
    article: state.articles.current,
    redirectTo: state.articles.redirectTo,
    shouldRedirect: state.articles.shouldRedirect
  }), 
  Actions
)(MyComponent);

PS:我在这里使用了一些babel语法糖,因此请确保您在.babelrc中设置了以下预设。

  • es2015
  • stage-2
  • stage-0
  • react