如何使用 React Apollo 2.1 的 Mutation 组件在装载上运行突变? [英] How to run a mutation on mount with React Apollo 2.1's Mutation component?

查看:21
本文介绍了如何使用 React Apollo 2.1 的 Mutation 组件在装载上运行突变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们目前正在从 Relay 转移到 React Apollo 2.1 和我正在做的事情似乎很可疑.

上下文:某些组件必须仅在用户通过身份验证(通过 API 密钥)时才能呈现,因此有一个 Authenticator 组件保护树的其余部分.

App.js 中,它是这样使用的(显然下面的所有片段都是最小的例子):

从'react'导入React;从'./Authenticator'导入验证器;从 './MyComponent' 导入 MyComponent;导出默认函数 App({ apiKey }) {返回 (<身份验证器 apiKey={apiKey}render={({ error, token }) =>{if (error) return 

{error.message}

;if (token) return ;返回<div>正在验证...</div>;}}/>);}

如果认证成功,MyComponent 会被渲染.Authentication 在第一次渲染/挂载时向服务器发送身份验证突变并调用 相应地渲染道具.Authentication.js 看起来像这样:

从'graphql-tag'导入gql;从反应"导入反应;从'react-apollo'导入{突变};const AUTH_MUTATION = gql`mutation Login($apiKey: String!) {登录(apiKey:$ apiKey){令牌}}`;导出默认函数 Authenticator({ apiKey, render }) {返回 (<Mutation mutation={AUTH_MUTATION} 变量={{ apiKey }}>{(登录,{数据,错误,调用}) =>{如果(!调用)登录();//⚠️ 这看起来很粗略 ⚠️const token = (data && data.login.token) ||不明确的;返回渲染({错误,令牌});}}</突变>);}

那个 if (! called) login(); 是什么让我停下来.如果我不指定 if (! called),UI 会变得癫痫并发送数千个请求(这是有道理的,调用 login() 会导致 render() 重新运行),但它应该如何使用?

这似乎是 Query等效组件的不同之处在于简单地呈现它发出请求.我想知道是否有一种方法可以将相同的机制应用于 Mutation,这需要调用 mutate 函数作为渲染道具的一部分.

与上述代码段等效的 Relay 与 React Apollo 的 QueryMutation 上所做的完全一样:

//Authentication.js从反应"导入反应;从'react-relay'导入{graphql,QueryRenderer};从中继运行时"导入{环境};//隐藏所有与 `Environment` 相关的样板文件const environment = return new Environment(/* ... */);const AUTH_MUTATION = graphql`mutation Login($apiKey: String!) {登录(apiKey:$ apiKey){令牌}}`;导出默认函数 Authenticator({ apiKey, render }) {返回 (<QueryRenderer 查询={AUTH_MUTATION} 变量={{ apiKey }}渲染={渲染}/>);}//App.js从反应"导入反应;从'./Authenticator'导入验证器;从 './MyComponent' 导入 MyComponent;导出默认函数 App({ apiKey }) {返回 (<身份验证器 apiKey={apiKey}render={({ error, props }) =>{if (error) return 

{error.message}

;if (props) return ;返回<div>正在验证...</div>;}}/>);}

解决方案

对或错,Apollo 对如何使用查询和突变做出了一些假设.按照惯例,查询只获取数据,而变异,好吧,变异数据.Apollo 将这一范式更进一步,并假设突变会发生以响应某种行为.因此,正如您所观察到的,Query 在挂载时获取查询,而 Mutation 向下传递一个函数以实际获取突变.

从这个意义上说,您已经偏离了这些组件应该使用"的方式.

我不认为你的方法有什么问题——假设 called 永远不会被重置,你的组件应该按预期运行.但是,作为替代方案,您可以创建一个简单的包装器组件来利用 componentDidMount:

class CallLogin 扩展 React.Component {componentDidMount() {this.props.login()}使成为() {//反应 16返回 this.props.children//老套 :)//返回 <div>{ this.props.children }</div>}}导出默认函数 Authenticator({ apiKey, render }) {返回 (<Mutation mutation={AUTH_MUTATION} 变量={{ apiKey }}>{(登录,{ 数据,错误})=>{const token = (data && data.login.token) ||不明确的;返回 (<CallLogin 登录={登录}>{渲染({错误,令牌})}</CallLogin>)}}</突变>);}

We are currently moving from Relay to React Apollo 2.1 and something I'm doing seems fishy.

Context: Some components must only be rendered if the user is authenticated (via an API key), so there is an Authenticator component guarding the rest of the tree.

In App.js, it gets used like this (obviously all snippets below are minimal examples):

import React from 'react';
import Authenticator from './Authenticator';
import MyComponent from './MyComponent';

export default function App({ apiKey }) {
  return (
    <Authenticator apiKey={apiKey}
      render={({ error, token }) => {
        if (error) return <div>{error.message}</div>;
        if (token) return <MyComponent token={token} />;
        return <div>Authenticating...</div>;
      }}
    />
  );
}

If authentication succeeds, MyComponent gets rendered. Authentication sends the authentication mutation to the server when rendered/mounted for the first time and calls the render prop accordingly. Authentication.js looks as such:

import gql from 'graphql-tag';
import React from 'react';
import { Mutation } from 'react-apollo';

const AUTH_MUTATION = gql`mutation Login($apiKey: String!) {
  login(apiKey: $apiKey) {
    token
  }
}`;

export default function Authenticator({ apiKey, render }) {
  return (
    <Mutation mutation={AUTH_MUTATION} variables={{ apiKey }}>
      {(login, { data, error, called }) => {
        if (!called) login(); // ⚠️ This seems sketchy ⚠️

        const token = (data && data.login.token) || undefined;
        return render({ error, token });
      }}
    </Mutation>
  );
}

That if (!called) login(); is what is giving me pause. If I don't specify if (!called), the UI becomes epileptic and sends thousands of requests (which makes sense, calling login() causes render() to re-run), but is that how it's supposed to be used?

It seems like the Query component equivalent differs in that simply rendering it emits the request. and I am wondering if there is a way to apply the same mechanism to Mutation, which requires calling the mutate function as part of the render prop.

The Relay equivalent of the snippet above does exactly what React Apollo's Query does on Mutation:

// Authentication.js

import React from 'react';
import { graphql, QueryRenderer } from 'react-relay';
import { Environment } from 'relay-runtime';

// Hiding out all the `Environment`-related boilerplate
const environment = return new Environment(/* ... */);

const AUTH_MUTATION = graphql`mutation Login($apiKey: String!) {
  login(apiKey: $apiKey) {
    token
  }
}`;

export default function Authenticator({ apiKey, render }) {
  return (
    <QueryRenderer query={AUTH_MUTATION} variables={{ apiKey }}
      render={render}
    />
  );
}


// App.js

import React from 'react';
import Authenticator from './Authenticator';
import MyComponent from './MyComponent';

export default function App({ apiKey }) {
  return (
    <Authenticator apiKey={apiKey}
      render={({ error, props }) => {
        if (error) return <div>{error.message}</div>;
        if (props) return <MyComponent token={props.loginAPI.token)} />;
        return <div>Authenticating...</div>;
      }}
    />
  );
}

解决方案

Right or wrong, Apollo makes some assumptions about how queries and mutations are used. By convention queries only fetch data while mutations, well, mutate data. Apollo takes that paradigm one step further and assumes that mutations will happen in response to some sort of action. So, like you observed, Query fetches the query on mount, while Mutation passes down a function to actually fetch the mutation.

In that sense, you've already deviated from how these components are "supposed to be used."

I don't think there's anything outright wrong with your approach -- assuming called never gets reset, your component should behave as intended. As an alternative, however, you could create a simple wrapper component to take advantage of componentDidMount:

class CallLogin extends React.Component {
  componentDidMount() {
    this.props.login()
  }

  render() {
    // React 16
    return this.props.children
    // Old School :)
    // return <div>{ this.props.children }</div>
  }
}

export default function Authenticator({ apiKey, render }) {
  return (
    <Mutation mutation={AUTH_MUTATION} variables={{ apiKey }}>
      {(login, { data, error }) => {
        const token = (data && data.login.token) || undefined;
        return (
          <CallLogin login={login}>
            {render({ error, token })}
          </CallLogin>
        )
      }}
    </Mutation>
  );
}

这篇关于如何使用 React Apollo 2.1 的 Mutation 组件在装载上运行突变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆