Skip to content
This repository has been archived by the owner on Feb 19, 2022. It is now read-only.

Question: calling an effect with updated state #55

Closed
jhsu opened this issue May 16, 2017 · 3 comments
Closed

Question: calling an effect with updated state #55

jhsu opened this issue May 16, 2017 · 3 comments

Comments

@jhsu
Copy link

jhsu commented May 16, 2017

so i'm trying to have two different provideStates for pagination, one that handles the pagination state, and one that does the fetching.

const withFetching = provideState({
  initialState: () => {
    return {
      entities: [],
    };
  },
  effects: {
    fetchItems: (effects, params) =>
      state => {
        // TODO: use { page , pageSize } from params
        return fetch('/things')
          .then(res => res.json())
          .then(({things}) => state => ({...state, entities: things}));
      },
  },
});

const justPagination = provideState({
  initialState: () => {
    return {
      page: 1,
      pageSize: 10,
    };
  },
  effects: {
    nextPage: effects => {
      return state => {
        const nextState = {...state, page: page + 1};
        return effects.fetchItems(nextState).then(() => nextState);
      };
    },
  },
});

const Container = withFetching(justPagination(YourComponent));

This doesn't work, but I'd like to do something like that. Any suggestions?

@TIMNHE
Copy link

TIMNHE commented May 20, 2017

Same issue here, when try to compose 2 providers with the same component:

const counterStore = provideState({
  initialState: () => ({counter: 0}),
  effects: {
    addCount: (effects, val) => state => Object.assign({}, state, {counter: state.counter + val})
  }
});

const colorStore = provideState({
  initialState: () => ({color: 'black'}),
  effects: {
    setColor: (effects, col) => state => Object.assign({}, state, {color: col})
  }
});

const CounterChildComponent = injectState(({state: {color, counter}, effects}) => {
  const handleClick = () => effects.addCount(1);
  return (
    <div className="childComponent" onClick={handleClick}>
      <span>CounterChild ...</span>
      <span>color: {color}, {counter}</span>
    </div>
  );
});

const ColourChildComponent = injectState(({state, effects}) => {
  const handleClick = () => effects.setColor('blue');
  return (
    <div className="childComponent" onClick={handleClick}>
      <span>ColorChild ...</span>
      <span>{state.color}</span>
    </div>
  );
});

const ParentComponent = (props) =>
  <div className="parent">
    <CounterChildComponent/>
    <ColourChildComponent/>
  </div>;

export default counterStore(colorStore((ParentComponent)));
// ramda compose does not work either -> 
// R.compose(counterStore, colorStore)(ParentComponent)

It's strange because when apply the colorStore in first place, you can update the colourChild but not the counterChild ...

... if you click on counterChild many times and then click on the color component, both states are updated

@divmain
Copy link
Contributor

divmain commented May 27, 2017

@jhsu, there's one problem with the code you posted, and that's probably the source of the problems you're encountering. The state update function is a synchronous function - it returns new state. However, your nextPage state update function returns a Promise. That's probably not what you intended. Something like this will work:

const serverData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const getData = page => {
  page = page || 0;
  return Promise.resolve(serverData[page]);
};

const withFetching = provideState({
  initialState: () => ({ entities: [0] }),
  effects: {
    fetchPage: async (effects, page) => {
      const pagedData = await getData(page);
      return state => {
        const entities = [].concat(state.entities);
        entities[page] = pagedData;
        return Object.assign({}, state, { entities });
      };
    }
  }
});

const justPagination = provideState({
  initialState: () => ({ page: 0 }),
  effects: {
    nextPage: async (effects, currentPage) => {
      const nextPage = currentPage + 1;
      await effects.fetchPage(nextPage);
      return state => Object.assign({}, state, { page: nextPage });
    }
  }
});

const App = injectState(({ state, effects }) => {
  const onClick = () => effects.nextPage(state.page);
  return (
    <div>
      <div>entities: { JSON.stringify(state.entities) }</div>
      <div>page: { state.page }</div>
      <button onClick={onClick}>click</button>
    </div>
  );
});

@divmain
Copy link
Contributor

divmain commented May 27, 2017

@TIMNHE, your issue is somewhat different. I believe it is fixed here and was the same bug as #42. This will be released on npm shortly.

@divmain divmain closed this as completed May 27, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants