ckinmind / react-cnode

👨🏻‍💻React构建的cnode社区(附详细问题说明)
https://ckinmind.github.io/react-cnode
15 stars 7 forks source link

关于在一个异步action中无法调用另一个异步action的问题(同步action可以) #12

Open ckinmind opened 7 years ago

ckinmind commented 7 years ago

背景是这样的:

给回复点赞的逻辑,是通过一个异步action, 在这个异步action中发送点赞请求,如果请求成功,则再调用获取当前topic的action, 这样可以重新加载一遍当前的topic以便使得点赞的更新显示出来,但是实际测试的过程中发现无法触发异步action中的异步action, 但是可以触发异步action中的同步action(另一个action)

starAction.js

import topicActions from './topicActions';

let starActions = {

    starReply: function(replyId, replyLoginname){
        return function (dispatch, getState) {
          .....

            fetch(`https://cnodejs.org/api/v1/reply/${replyId}/ups`, {
                method: 'POST',
                headers: {"Content-Type": "application/x-www-form-urlencoded"},
                body: `accesstoken=${accessToken}`
            })
                .then(response => response.json())
                .then(json => {
                    if (json.success) {
                        topicActions.testTecchingTopic(); //同步action, 测试通过
                        topicActions.fetchingTopic();     // 异步action, 没有响应
                    } else {
                       ....
                    }
                })
        }
    }
};

export default starActions;

topicActions.js


let topicActions = {

        /** 加载topic异步操作*/
        fetchingTopic: function (topicId) {
            return function (dispatch, getState) {
              console.log('这段话没打印出来了');
            }
        },

    ....

        testTecchingTopic: () => {
            console.log('这段话打印出来了');
        }
    }
;

export default topicActions;
ckinmind commented 7 years ago

上面触发另一个action的方式,是这样的

topicActions.testTecchingTopic(); //同步action, 测试通过
topicActions.fetchingTopic();     // 异步action, 没有响应

没有用dispatch方法,测试的结果是同步的能打印出结果,异步的没反应

如果使用dispatch

  dispatch(topicActions.testTecchingTopic());   //同步
  dispatch(topicActions.fetchingTopic(json));   //异步

同步的有打印结果 异步的报错:createStore.js?66b2:166 Uncaught (in promise) Error: Actions must be plain objects. Use custom middleware for async actions

Jeepeng commented 7 years ago

需要dispatch的,看起来没什么问题。。。 从报错看来好像是引入thunk中间件没有成功 看了你的代码createStore函数用法错了:

const store = createStore(
    reducer,
    applyMiddleware(thunk)
);

应该:

// 调用 applyMiddleware,使用 middleware 增强 createStore:
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)

const store = createStoreWithMiddleware(reducer)

http://redux.org.cn/docs/api/createStore.html http://redux.org.cn/docs/api/applyMiddleware.html

ckinmind commented 7 years ago

@Jeepeng 感谢解答,还是有几个问题

  1. thunk中间件引入应该没问题,因为这里涉及两个异步action, 一个是starActions,一个是topicActions,而topicActions是starActions中调用的,实际执行过程中starActions这个异步action可以执行的,所以应该没问题

  2. createStore的写法问题,我的写法是参照redux-thunk中的写法

    
    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) );



3. 我不是很清楚你的那种写法和我的写法有什么区别

最后还是感谢解答
Jeepeng commented 7 years ago

确实,redux@>=3.1.0可以这样用createStore, 但是看了一下redux源代码:报错位置 确实像是由于某种情况没有成功引入redux-thunk

ckinmind commented 7 years ago

@Jeepeng 感谢解答,我再研究研究

WoolYang commented 7 years ago

我也遇到了同样的问题,请问怎么解决的?

ckinmind commented 7 years ago

@WoolYang 这个是很久之前的问题了,我其实也忘了怎么解决的,估计我也没解决这个问题,现在我也不再使用redux, 而是使用另一个数据管理的库mobx, 强行安利,很好用,具体可以看我的这两个分享:

  1. apple-basket-redux

    分别用redux和mobx实现了同一个demo, 你可以对比两种实现方式

  2. mobx-share

    关于mobx的分享,帮助你快速掌握mobx

希望能对你有帮助

RocketV2 commented 6 years ago

也遇到过此问题,不过我的dispatch代码如下(成功执行):

ReduxStore.dispatch((dispatch,getState)=>{
    // 做异步处理
    setTimeout(()=>{
        dispatch({type:'ADD_ITEM',text:val});
    },2000)

})

官方给出的示例代码为:

function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 1000);
  };
}

尽管少了一层return 函数,但是确实能够获取到dispatch/getState,代码能够执行;redux-thunk的源码为:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

将函数createThunkMiddleware改变一下,不难发现,我们通过dispatch传入的其实就是action,
如果action 类型为function,就会执行我们传入的函数,并且将dispatch/getState参数传入我们传入的函数中;

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) =>{
    return (next) =>{
        return (action) =>{
            if (typeof action === 'function') {
              return action(dispatch, getState, extraArgument);
            }

            return next(action);
        }
    }
  }
}

如果我的理解有偏差,欢迎交流探讨~~

ckinmind commented 6 years ago

@RocketV2 感谢回复,但是这个问题已经是很久之前的问题了,而且现在我已经很久很久没有使用redux了,现在用的mobx, 很好用