无限循环与 redux-saga

Infinite loop with redux-saga

我正在使用 react-router v4 和 redux-saga。我试图在页面加载时进行 API 调用。当我访问 /detailpage/slug/ 时,我的应用程序似乎陷入循环并无休止地调用我的 API 端点。这是我的应用程序的设置方式。假设我的导入是正确的。

index.js

const history = createHistory()
const sagaMiddleware = createSagaMiddleware()
const middleware = [routerMiddleware(history), sagaMiddleware]

const store = createStore(
  combineReducers({
    aReducer
  }),
  applyMiddleware(...middleware)
)

injectTapEventPlugin();
sagaMiddleware.run(rootSaga)


ReactDOM.render(
  <Provider store={store}>
  <ConnectedRouter history={history}>
    <Switch>
      <Route path="/" exact component={HomePage} />
      <Route path="/detailpage/:slug" component={Detail} />
      <Route path="/page" component={Page} />
    </Switch>
  </ConnectedRouter>
  </Provider>,
  document.getElementById('root')
);

reducers/index.js

const aReducer = (state={}, action) => {
  switch(action.type) {
    case 'SHOW_DETAIL':
    console.log('Reducers: reducer called')
    return Object.assign({}, state, { name: 'adfsdf' })
  default:
    return state;
  }
}
export default aReducer;

actions/index.js

export const showDetailInfo = () => {
  console.log('action called')
  return {
    type: 'SHOW_DETAIL'
  }
}

saga.js

export function* fetchDetailsAsync() {
  try {
    console.log('fetching detail info')
    const response = yield call(fetch, 'http://localhost:8000/object/1/', {
    method: 'GET',
    headers: {
      'Authorization': 'Token xxxxxxxxxxxx'
    }})

    console.log(response);

    yield put({type: 'SHOW_DETAIL', response: response.data})
  } catch (e) {
    console.log('error')
  }
}

// watcher saga
export function* fetchDetails() {
  console.log('watcher saga')
  yield takeEvery('SHOW_DETAIL', fetchDetailsAsync)
}

export default function* rootSaga() {
  console.log('root saga')
  yield [
    fetchDetails()
  ]
}

containers/Detail.js

const mapStateToProps = (state) => {
  return {
    name: 'Test'
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  console.log('mapDispatchToProps')
  return {
    showDetailInfo: (payload) => {
      console.log('dispatching');
      dispatch({ type: 'SHOW_DETAIL' })
    }
  }
}

const Detail = connect(
  mapStateToProps,
  mapDispatchToProps
)(DetailPage)

export default Detail;

components/DetailPage.js

class DetailPage extends React.Component {

  componentWillMount() {
    this.props.showDetailInfo();
  }

  render() {
    return (
      <Layout>
      <h3>DetailPage</h3>
      <p>{ this.props.name }</p>
      </Layout>
    )
  }
}

DetailPage.PropTypes = {
  showDetailInfo: PropTypes.func.isRequired,
  name: PropTypes.string.isRequired
}

export default DetailPage;

我花了几天时间进行故障排除,尝试了各种想法,包括测试不同的生命周期方法,从 applyMiddleware 中删除 routerMiddleware。

我认为我的组件在每次 API 调用后都在更新,但是任何生命周期方法的 console.log 表明它不是。

作为 React 生态系统的新手,这里有很多活动部件,对我来说排除故障很有挑战性。

当然,您在下一行中明确设置了无限循环:

yield put({type: 'SHOW_DETAIL', response: response.data})
// ...
yield takeEvery('SHOW_DETAIL', fetchDetailsAsync)

saga 不会为您做任何神奇的事情,它只是订阅和生成操作和执行协程的初级层。

解决方案:

对于从 React 组件捕获的操作,以及用于乐观和实际更新状态的操作,您应该使用不同的名称。

使用 yield takeEvery('SHOW_DETAIL_REQUEST', fetchDetailsAsync) 并以此方式命名您的操作。

在成功响应中使用 yield put({type: 'SHOW_DETAIL_SUCCESS', response: response.data}) 并以此方式命名您的减速器

不仅如此,您还可以对失败的 saga 请求使用 'SHOW_DETAIL_FAILURE'

以上名称均为通用名称

我知道您找到了答案,太好了。我有相同的症状,但问题不同,解决方案也不同。

我没有为我的动作使用普通常量,我使用的是来自我的动作的常量,这样我只需要将它写在一个位置。这是我的设置。但是我今天意识到一个问题。我的代码如下所示

    export const deleteArendeAction = {
      will: arende => ({
        type: "WILL_TA_BORT_ARENDEN",
        ...arende,
      }),
      did: payload => ({
        type: "DID_TA_BORT_ARENDEN",
        ...payload,
      }),
      error: payload => ({
        type: "DID_DELETE_ARENDE_ERROR",
        ...payload,
      }),
    }

    function* deleteArenden(arende) {
      try {
        yield arendeApi.deleteArende(arende.id)
      } catch (error) {
        yield put(deleteArendeAction.error(arende))
        return
      }
      yield put(deleteArendeAction.did(arende))
    }

    export function* deleteArendeSaga() {
        yield takeEvery(deleteArendeAction.will().type, deleteArenden)
    }

我的代码看起来像那样。 它不断触发我的 takeEvery 无限。 原来,yield put(deleteArendeAction.did(arende)) 这部分是罪魁祸首。因为变量 arende 的值为 { type: "WILL_TA_BORT_ARENDEN", ... } 导致了某种错误,再次触发了事件。 从技术上讲,我认为这不应该发生?但确实如此。 因此,如果您遇到这个问题,而答案并不能解决您的问题。然后仔细检查您发送到 put 的内容:P