2

In my component, I am using react-adopt, to compose graphql queries and mutations, so that my render props don't get too messy. I have the following code:

This is my mutation, it takes one argument - planID.

const CREATE_ORDER_MUTATION = gql`
  mutation CREATE_ORDER_MUTATION($planID: String!) {
    createOrder(planID: $planID) {
      planID
      name
      description
      subscriptionType
      images
      subscriptionType
      pricePerMonth
}
}

This is the adopt function, it takes a couple of mutations, one of which is createOrder. The way Apollo works is that I need to pass variables prop to createOrder component here. The problem is, I don't have the planID at this point. planID is available only inside of the actual component.

const Composed = adopt({
  toggleCart: <Mutation mutation={TOGGLE_CART_MUTATION} />,
  createOrder: <Mutation mutation={CREATE_ORDER_MUTATION} />,
});

My component looks like this. I have the planID available here, but how can I pass it as an argument to mutation?!

render() {
const { plan } = this.props;
return (
  <Composed>
    {({ toggleCart, createOrder }) => {
      const handleAdd = () => {
        toggleCart();
        Router.push('/waschingmachine');
        createOrder();
      };
      return (
        <StyledPlan>
          <h1>{plan.name}</h1>
          <p>{plan.description}</p>
          <img src={`static${plan.images[0]}`} alt="blue1" />
          <h2>{plan.pricePerMonth / 100} EUR</h2>
          <div className="buttons">
            <Link
              href={{
                pathname: '/plan',
                query: { id: plan.id },
              }}
            >
              <button type="button">info</button>
            </Link>
            <button onClick={handleAdd} type="button">
              Select Plan
            </button>
          </div>
        </StyledPlan>
      );
    }}
  </Composed>
);
}

If there is no way to solve it this way, how would you approach it differently?

2 Answers 2

4

Here is how you can pass arguments through react-adopt way into your inner mutations mapper:

// In a nutshell, `react-adopt` allows you to pass props to your `Composed`
// component, so your inner `mapper` can access those props to pass them to 
// your <Query> or <Mutation>

// Here's your initial `Composed` component from above
const Composed = adopt({
  // `planId` is passed from below via props (see `<ContainerComponent />) 
  toggleCart: ({ planId, render }) => (
    <Mutation mutation={TOGGLE_CART_MUTATION} variables={{ planId }}>{render}</Mutation>,
    ),
  // `planId` is passed from below via props (see `<ContainerComponent />)
  createOrder: ({ planId, render })=> (
    <Mutation mutation={CREATE_ORDER_MUTATION} variables={{ planId }}>{render}</Mutation>                 
  )
});

// `<ContainerComponent />` will take a plan as its props and passed `planId` to 
// the `<Composed />` component
const ContainerComponent = ({ plan }) => (
  <Composed planId={plan.id}>
    {({ toggleCart, createOrder }) => {      
      const handleAdd = e => {
        e.preventDefault();
        toggleCart();
        // ...
        createOrder();
        // ...
      };

      // Whatever your UI needs you can pass them here via here
      return <YourPresentationComponent onClick={handleAdd} />;
    }}
  </Composed>
)

// Now all you need to do is to pass `plan` from your render() to the 
// <ContainerComponent /> (This is the render() where you return your 
render() {
  const { plan } = this.props;
  return <ContainerComponent plan={plan} />
}

Hopefully this can be helpful to solve your issue! Just a side note, you can also get the previous mapper value and pass them onto the next mapper fn as part of the argument, see below:

const Composed = adopt({ 
  // Here you can retrieve the actual `plan` by using `userId` and pass the result 
  // into your next children mapper fn to consume
  plan: ({ userId, render }) => (
   <Query query={GET_PLAN_GRQPHQL} variables={{ userId }}>{render}</Query>
  ),
  // `plan` is actually coming from above <Query /> component 
  toggleCart: ({ plan, render }) => { //... },
  createOrder: ({ plan, render }) => { //...}
});
Sign up to request clarification or add additional context in comments.

1 Comment

As far as I can tell this should be the accepted answer. This demonstrates how to pass a variable to a <Query /> component
1

The mutate function passed in the rendered children can be called with options. Options can include variables used in the GraphQL mutation string. [1].

This means that you can call the createOrder mutation function like so.

createOrder({ variables: { planID: 'some plan id' } });

Given the dynamic nature of planID, there are a number of ways to implement this. One of which is to use data attributes as below:

A data attribute can be set on for the plan id on the button .

<button onClick={handleAdd} data-planid={plan.id} type="button">
      Select Plan
</button>

handleAdd can be refactored to get the planid from the target dataset attribute and invoke createOrder with planID variable.

const handleAdd = event => {
    const planID = event.target.dataset.planid;
    toggleCart();
    Router.push('/waschingmachine');
    createOrder({ variables: { planID } });
};

Another is to directly pass planID to handleAdd when calling it in the onClick prop for the button.

<button onClick={() => handleAdd(plan.id)} type="button">
      Select Plan
</button>

Then update the handler

const handleAdd = planID => {
    toggleCart();
    Router.push('/waschingmachine');
    createOrder({ variables: { planID } });
};

There are tradeoffs to both approaches. For the earlier approach, the planid are set in the DOM as attributes and can be read later. While for the later one, N handlers are created for N plans and are kept in memory.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.