FormidableLabs / freactal

Clean and robust state management for React and React-like libs.
MIT License
1.65k stars 46 forks source link

State not being updated #34

Closed KadoBOT closed 7 years ago

KadoBOT commented 7 years ago

State is available to the component, so does the effects. (They are in different folders, effects is one folder above, dunno if that makes any difference to my problem).

// initial-state.js
export default () => ({
  selected: "internal",
  isOpen: true
})
// effects.js
export default {
  handleDialog: () => state => ({ ...state, isOpen: !state.isOpen })
}

PS: I've tried SoftUpdate and Object.assign as well in the above function.

// React Component that triggers the effects
const NavButtons = ({ effects }) => (
  <Wrapper>
    <RaisedButton primary label="Request Appraisal" onClick={effects.handleDialog} />
  </Wrapper>
);

NavButtons.propTypes = {
  effects: PropTypes.shape({
    handleDialog: PropTypes.func.isRequired
  }).isRequired
};

export default injectState(NavButtons);
// React component that holds the mentioned state
const RequestAppraisal = ({ state, effects }) => {
  const actions = [
    <RaisedButton label="Submit" primary onTouchTap={effects.handleDialog} />
  ];

  return (
    <Dialog
      autoScrollBodyContent
      title="Create new feedback round"
      actions={actions}
      modal={false}
      open={state.isOpen}
    >
      <RowWrapper>
        <RowTitle>Survey Template</RowTitle>
        <Wrapper>
          <SurveyTemplate />
        </Wrapper>
      </RowWrapper>
      <RowWrapper>
        <RowTitle>Type of Feedback</RowTitle>
        <Wrapper>
          <TypeOfFeedback />
        </Wrapper>
      </RowWrapper>
      <RowWrapper>
        <RowTitle>Add Assessors</RowTitle>
        <Wrapper>
          <AddAssessors />
        </Wrapper>
      </RowWrapper>
      <RowWrapper>
        <Wrapper>
          <Assessors />
        </Wrapper>
      </RowWrapper>
    </Dialog>
  )
}

RequestAppraisal.propTypes = {
  state: PropTypes.shape({
    isOpen: PropTypes.bool.isRequired
  }).isRequired,
  effects: PropTypes.shape({
    handleDialog: PropTypes.func.isRequired,
  }).isRequired
};

export default provideState({
  initialState
})(injectState(RequestAppraisal));

The problem is that the despite being available to the component, the state is only updated if I use the state/effect in the parent component. If I just inject/provide without using them, the state does not update.

Here is an example: The component above doesn't update if the App.js written like this:

const Dashboard = () => (
  <div>
    <NavBar />
    <Wrapper>
      <UserInfo />
      <DashboardData />
      <RequestAppraisal />
    </Wrapper>
  </div>
);

export default withState(injectState(Dashboard));

But it does update if App.js has props state:

// NavButtons component is inside NavBar.
const Dashboard = ({ state }) => (
  <div>
    {console.log(state.isOpen)}
    <NavBar />
    <Wrapper>
      <UserInfo />
      <DashboardData />
      <RequestAppraisal />
    </Wrapper>
  </div>
);

export default withState(injectState(Dashboard));

Is that the correct behaviour? Because like in my example, I don't need to use the state/effects in the parent component. Or I am doing something wrong?

Thanks

KadoBOT commented 7 years ago

So, I've figured that an effect doesn't know the state of its children, and that's why it's not working

TL;DR: Is it possible to update a child state from a parent effects?

divmain commented 7 years ago

Hi @KadoBOT, it is not possible to update child state from a parent effect. The idea is that a piece of state and the effects that transform it should be co-located. If a piece of state needs to live closer to the root of the component tree, so should the corresponding effect, and vice versa.

If I'm reading your example correctly, I would say that the isOpen state atom "belongs" to the Dashboard. It is updated from the grand-child <NavButtons /> and consumed by child <Dashboard />.

Generally speaking, if you want to access and/or transform a piece of state from more than one component in different parts of your application, the effect and state atom should be owned by the closest common ancestor of those components.

I hope that helps. Feel free to reach out again if I need to clarify or dig into anything in particular.