Search code examples
reactjsredux-sagareact-boilerplate

Proper Way in Fetching Initial Data in React Boilerplate Container


I am new with React Boilerplate and am using containers and saga to fetch initial data of a page. I am faced with 2 problems when implementing this.

UsersPage\index.js

// imported all sagas, etc

export function UsersPage({
  dispatch,
  usersPage,
  onGetUsers,
  onChangeSearch,
  onSetActive,
}) {

  useInjectReducer({ key: 'usersPage', reducer });
  useInjectSaga({ key: 'usersPage', saga });

  onGetUsers();

  return (...);
}

UsersPage.propTypes = {
  dispatch: PropTypes.func.isRequired,
  usersPage: PropTypes.object,
  onGetUsers: PropTypes.func,
  onChangeSearch: PropTypes.func,
  onSetActive: PropTypes.func,
};

const mapStateToProps = createStructuredSelector({
  usersPage: makeSelectUsersPage(),
});

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    onGetUsers: () => dispatch(getUsers()),
    onChangeSearch: evt => dispatch(changeSearch(evt.target.value)),
    onSetActive: active => dispatch(setActive(active)),
  };
}

const withConnect = connect(
  mapStateToProps,
  mapDispatchToProps,
);

export default compose(withConnect)(UsersPage);

UsersPage\actions.js

...
export function getUsers() {
  console.log('action getUsers');
  return {
    type: constants.GET_USERS,
  };
}
...

UsersPage\saga.js

export function* getUsers() {
  console.log('saga getUsers');
  try {
    const response = yield call(api.users.getAll);
    yield put(
      getUsersSuccess({
        users: response.data.data,
      }),
    );
  } catch (err) {
    const error = err.response.data.message;
    yield put(getUsersFail(error));
  }
}

export default function* usersPageSaga() {
  yield takeLatest(constants.GET_USERS, getUsers);
}

When I first loaded the page, the console log shows the following:

action getUsers
saga watcher

However, when I navigated to other page, and then move to this page:

action getUsers
saga worker getUsers

PROBLEM 1

It seems that when it loads the first time, the saga watcher gets called later than the action (getUsers). And, thus, it fails on the first load but on subsequent load, the saga worker gets called just fine. How can I ensure that saga watcher gets called first before the action?

PROBLEM 2

Because the action (getUsers) is inside the render, it gets called everytime when there is an update. Therefore, when getUsers return and apply the new data into the reducer, the render gets called again and the action (getUsers) gets called again causing endless cycle. What should we do or what is the proper way to actually load initial data in the container?


Solution

  • To fix the first issue try using takeEvery instead of takeLatest:

    export default function* usersPageSaga() {
      yield takeEvery(constants.GET_USERS, getUsers);
    }
    

    Regarding the second issue, you can load the initial data with useEffect hook:

    useEffect(()=> {
      // load the data
    }, [])