如何 运行 使用 React Apollo 2.1 的 Mutation 组件挂载突变?
How to run a mutation on mount with React Apollo 2.1's Mutation component?
我们目前正在从 Relay to 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
在第一次 rendered/mounted 时向服务器发送身份验证突变,并相应地调用 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
component equivalent 的不同之处在于它只是简单地呈现它发出的请求。我想知道是否有一种方法可以将相同的机制应用于 Mutation
,这需要调用 mutate 函数作为 render prop 的一部分。
上面代码片段的 Relay 等效项与 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
传递一个函数来实际获取突变。
从这个意义上说,您已经偏离了这些组件的方式 "supposed to be used."
我不认为您的方法有任何完全错误——假设 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
我们目前正在从 Relay to 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
在第一次 rendered/mounted 时向服务器发送身份验证突变,并相应地调用 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
component equivalent 的不同之处在于它只是简单地呈现它发出的请求。我想知道是否有一种方法可以将相同的机制应用于 Mutation
,这需要调用 mutate 函数作为 render prop 的一部分。
上面代码片段的 Relay 等效项与 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
传递一个函数来实际获取突变。
从这个意义上说,您已经偏离了这些组件的方式 "supposed to be used."
我不认为您的方法有任何完全错误——假设 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