Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
870 views
in Technique[技术] by (71.8m points)

javascript - Infinite loop with redux-saga

I'm using react-router v4 and redux-saga. I'm attempting to make an API call when a page loads. When I visit /detailpage/slug/, my application seems to get stuck in a loop and makes endless calls to my API endpoint. Here's how my application is setup. Assume my imports are correct.

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;

I've spent a few days troubleshooting, trying various ideas including testing different lifecycle methods, removing the routerMiddleware from applyMiddleware.

I thought that my component was updating after every API call, but console.log from any of the lifecycle methods indicates that it's not.

Being new to react ecosystem, there are a lot of moving parts here and it's challenging for me to troubleshoot.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Of course, you explicitly set the infinite loop the next lines:

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

The saga doesn't do any magic things for you, and only is a preliminary layer of a subscription and generation on actions and executions coroutines.

SOLUTION:

You shall use different names for actions which you catch from React components, and actions which are used for optimistical and real up-dating of a status.

Use yield takeEvery('SHOW_DETAIL_REQUEST', fetchDetailsAsync) and name your action in this manner.

Use yield put({type: 'SHOW_DETAIL_SUCCESS', response: response.data}) in success response and name your reducer in this manner

More than that, you can use 'SHOW_DETAIL_FAILURE' for failed saga request.

All names above are common-used case.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...