現在、 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のQuery
が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>;
}}
/>
);
}
_
正しいか間違っているか、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>
);
}
ここに、私のわずかにユニークなケースのための私の実装があり、それはうまくいきます:
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