web-dev-qa-db-ja.com

React Apollo 2.1のMutationコンポーネントを使用してマウントでミューテーションを実行する方法は?

現在、 Relay から React Apollo 2.1 に移行しており、私がやっていることは怪しいようです。

コンテキスト:一部のコンポーネントは、ユーザーが(APIキーを介して)認証された場合にのみレンダリングする必要があるため、Authenticatorコンポーネントガードがありますツリーの残りの部分。

_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, token }) => {
        if (error) return <div>{error.message}</div>;
        if (token) return <MyComponent token={token} />;
        return <div>Authenticating...</div>;
      }}
    />
  );
}
_

認証が成功すると、MyComponentがレンダリングされます。 Authenticationは、初めてレンダリング/マウントされたときにサーバーに認証変更を送信し、それに応じて render prop を呼び出します。 _Authentication.js_は次のようになります。

_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>
  );
}
_

if (!called) login();が一時停止の原因です。 if (!called)を指定しない場合、UIはてんかんになり、数千のリクエストを送信します(これは理にかなっています、login()を呼び出すとrender()が再実行されます)。しかし、それはどのように使用されることになっていますか?

Queryコンポーネントと同等 は、単にレンダリングするだけでリクエストが発行されるという点で異なるようです。そして、同じメカニズムをMutationに適用する方法があるのだろうかと思います。これには、レンダリングプロップの一部としてmutate関数を呼び出す必要があります。

上記のスニペットと同等のリレーは、React ApolloのQueryMutationで行うこととまったく同じことを行います。

_// 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>;
      }}
    />
  );
}
_
16
astorije

正しいか間違っているか、Apolloはクエリとミューテーションの使用方法についていくつかの仮定を行います。慣例により、クエリはデータをフェッチするだけです。アポロはそのパラダイムをさらに一歩進め、何らかのアクションに応じて突然変異が起こると想定しています。したがって、あなたが観察したように、Queryはマウント時にクエリをフェッチし、Mutationは実際に突然変異をフェッチする関数を渡します。

その意味で、これらのコンポーネントが「使用されると想定される」方法からすでに逸脱している。

あなたのアプローチにまったく問題はないと思います-calledが決してリセットされないと仮定すると、コンポーネントは意図したとおりに動作するはずです。ただし、代わりに、単純なラッパーコンポーネントを作成して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>
  );
}
18
Daniel Rearden

ここに、私のわずかにユニークなケースのための私の実装があり、それはうまくいきます:

const VERIFY_USER = gql`
    mutation Verify_User($token: String!){
        verifyUser(token:$token){
            token
        }
    }
`

class Authenticator extends Component {

  state = { redirect: false }

  redirect = (authStatus) => {
    this.setState({redirect:true})
 }

  render() {
    if(this.redirect){
      return <Redirect to="/" />
    }

    const { token } = this.props.match.params
    console.log(token, "the token sir")

    return (
      <Mutation mutation={VERIFY_USER} variables={{ token }}>
        {(mutation) => {
          return (
            <VerifyUser authenticateUser={mutation} redirect={this.redirect}/>
          )
        }}
      </Mutation>
    )
  }
}


class VerifyUser extends Component {

  componentDidMount(){
    this.authUser();
  }

  authUser = async () => {
    let userStatus = await this.props.authenticateUser() // call the mutation
    this.props.redirect(userStatus)
  }

  render() {
    return null
  }
}

export default Authenticator
0
Lawrence Eagles