apollographql / reason-apollo

Reason binding for Apollo Client and React Apollo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to make side effects after mutation?

levinqdl opened this issue · comments

Currently we are passing component logic as children of Mutation, when it comes to make some side effects according to the mutation result, such as, when the mutation success, call a prop passed from parent component, which will modify state of the parent component. In the situation, I will receive a warning:

Warning: setState(...): Cannot update during an existing state transition

// Parent.re
let make = _children => {
  ...component,
  initialState: () => {isLogin: false},
  reducer: ((), state) => ReasonReact.Update({isLogin: ! state.isLogin}),
  render: self => <Login onSuccess=self.send />
};
// Login.re
let make = (~onSuccess, _children) => {
  ...component,
  initialState: () => {username: "", password: ""},
  reducer: (action, state) =>
    switch action {
    | LoginSuccess =>
      onSuccess();
      ReasonReact.NoUpdate;
    },
  render: self =>
    <Mutation>
      ...(
           (mutate, result) => {
             let {username, password} = self.state;
             let mutation = LoginMutation.make(~username, ~password, ());
             let result =
               switch result {
               | NotCalled => <Text value="not called" />
               | Loading => <Text value="loading" />
               | Failed(error) => <Text value=error />
               | Loaded(res) =>
                 let parse = mutation##parse;
                 let login = parse(res)##login;
                 switch login {
                 | None => ReasonReact.nullElement
                 | Some(_) =>
                   self.send(LoginSuccess);  // side effect here
                   ReasonReact.nullElement;
                 };
               };
             <View>
                 <TouchableHighlight onPress=(() => mutate(mutation))>
                   <Text value="Login" />
                 </TouchableHighlight>
                 result
             </View>;
           }
         )
    </Mutation>
}

I have tried to put the Mutation in Parent.re and render Login.re with a prop to trigger the mutation, this way avoids the warning. Is this a good way to solve the problem? Or maybe we can pass a callback to handle this, like ApolloClient.mutation(options) can pass a update.

It would be cool to be able to add Mutation/Query actions (variants) to a reducer. Similarly to the way the compose function works in Redux. I'm pretty new to Reason, but willing to help out a create a PR if someone has some pointers or advice on the best way to do this. :)

@jbaxleyiii Could you give an example on how we would do this in react-apollo please?
Should we add a component inside the render function that calls a parent function on componentDidUpdate?

For the given example, I'd suggest using the fact that mutate returns a promise with the mutation result rather than the result object getting passed into the render function. You don't want the side-effect to fire each time the component renders with result === Loaded, just one time immediately after the mutation completes. I'm pretty sure firing the update after the promise resolves will get around the setState error nicely, and is probably less error-prone to boot.

Sounds similar to the problem I'm facing #124

can you use Mutation onComplete or onError props?