I wonder, what is the way to use new Mutation components with react lifecycle methods.
Say I've got a page where I use several react-apollo mutations. I want to execute another mutation when loading state changes from true to false to show a notification popup in the page corner.
With higher order component I would do that in componentDidUpdate method, but now with <Mutation /> component I can't do that. Am I missing anything?
You can render your component in render prop function and pass mutation in props:
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
const ADD_TODO = gql`
mutation addTodo($type: String!) {
addTodo(type: $type) {
id
type
}
}
`;
const AddTodo = () => {
return (
<Mutation mutation={ADD_TODO}>
{(addTodo, { data }) =>
<YourComponent someFunction={addTodo} />
}
</Mutation>
);
};
class YourComponent extends Component {
componentDidMount(){
this.props.someFunction({variables: {type: 'abc'}})
}
render(){
return <div></div>
}
}
Related
This is my first time using Apollo and React so I'll try my best.
I have a GraphQl API from which I consume some data through ApolloClient mutations. The problem is that I don't know how to show the resulting information outside of the .result. I've tried to do so with a class that has a function to consume some data and a render to show it.
The mutation works and shows the data on the console but the page remains blank when the page is loaded, so the problem I've been stuck on is, how do I show this data?
Btw, if there's any advice on how to insert data from a form using this same mutation method I'd pretty much appreciate it.
Thanks in advance.
import React, { useEffect, useState, Component } from 'react';
import { graphql } from 'react-apollo';
import './modalSignUp.css';
import{header} from './Header.js';
import ReactDOM from "react-dom";
import { ApolloProvider, Query, mutation } from "react-apollo";
import { ApolloClient, InMemoryCache, gql, useMutation } from '#apollo/client';
export const client = new ApolloClient({
uri: 'http://localhost:4011/api',
cache: new InMemoryCache(),
});
client.mutate({
mutation: gql`
mutation signin{
login(data:{
username:"elasdfg",
password:"12345678"}){
id,roles,email,username}
}
`
}).then(result => console.log(result));
export class UserList extends Component {
displayUsers() {
console.log(this.result)
var data = this.props.data;
return data.login.map((user) => {
return (
<li>{user.email}</li>
);
})
}
render() {
return (
<div>
<li>
{this.displayUsers()}
</li>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Header />);
Mutation result
I've tried to use a class to fetch the data given by the mutation and later render it in the component. I've also tried passing the result to a variable but I had no success with that.
I'm just expecting to see the data resulting from the mutation
You should request data inside the component and then save it to the state.
export class UserList extends Component {
constructor() {
this.state = {
newData: null,
};
this.mutateData = this.mutateData.bind(this);
}
mutateData() {
client
.mutate({
mutation: gql`
mutation signin {
login(data: { username: "elasdfg", password: "12345678" }) {
id
roles
email
username
}
}
`,
})
.then((result) => {
this.setState({ newData: result });
});
}
componentDidMount() {
this.mutateData();
}
render() {
// do something with new data
}
}
I am using ApolloClient GraphQl query inside react class to fetch data from server:
import React, {Component} from 'react';
import {useCompanyLogo} from '../../queries/companyLogo';
class Logo extends Component {
constructor() {
super();
this.state = {logo: ""};
}
componentDidMount() {
const {error, loading, data} = useCompanyLogo();
if(loading) return <div>spinner</div>
if(error) return <div>error!</div>
const imageSource = data.companyLogo[0].image.urls[0];
this.setState({logo: imageSource});
}
render() {
return (
<div className="logo-area">
<img src={"http://computer-313:5000" + this.state.logo} alt="Businex-Logo" style={{width:"80px"}} />
</div>
);
}
}
export default Logo;
And the query is as below:
import {useQuery, gql} from "#apollo/client";
var COMPANY_LOGO = gql`
query CompanyLogo {
companyLogo {
image {
urls(first: 1)
}
}
}
`;
export const useCompanyLogo = () => {
const {error, data, loading} = useQuery(COMPANY_LOGO);
console.log(error, data, loading);
return {
error,
data,
loading
}
}
Everything works good when I use function instead of class But when I run this code I get the following error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
According to the React.js documentation you cannot use Hooks inside of Class Components.
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
You can try to use high order components and be able to pass the hooks into your Class Component that way.
After a user creates a profile, they receive a link in their email that sends them back to the site with a verifyToken in the url. If the token matches the token that is stores in the database, their isVerified status is stored in the database with the value true.
new-profile.js
import VerifyEMail from '../components/VerifyEmail';
const NewProfilePage = props => (
<div>
<VerifyEMail verifyToken={props.query.verifyToken} />
</div>
);
export default NewProfilePage;
Currently, I have this implemented and working using a form with a "Verify" button that the user must click to call the graphQL mutation, verifyEmail. Since this sets the isVerified value to true in the database, I know that everything is working as it should.
../components/VerifyEmail.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Mutation } from 'react-apollo';
import gql from 'graphql-tag';
const VERIFY_EMAIL_MUTATION = gql`
mutation VERIFY_EMAIL_MUTATION($verifyToken: String!) {
verifyEmail(verifyToken: $verifyToken) {
isVerified
}
}
`;
class VerifyEmail extends Component {
render() {
const { verifyToken } = this.props;
return (
<Mutation mutation={VERIFY_EMAIL_MUTATION} variables={{ verifyToken }}>
{verifyEmail => (
<form
onSubmit={async () => {
await verifyEmail(verifyToken);
}}
>
<button type="submit">Verify</button>
</form>
)}
</Mutation>
);
}
}
VerifyEmail.propTypes = {
verifyToken: PropTypes.string.isRequired,
};
export default VerifyEmail;
However, I really don't want to force my users to have to click a button to fire the mutation. I would like it to be called once the component loads. I have been racking my brain for a day and a half on this, tried so many things, and just can't seem to find anything that works.
I've seen some solutions using React hooks, Apollo hooks, componentDidMount, etc. My mind is just having a difficult time seeing it any more. This link had some of the best solutions that I found so far, but I couldn't figure out how to implement them...
[Feature idea] Execute a mutation on mount #1939
Any help to point me in the right direction would be really appreciated. Thank you.
This is far simpler application when using React hooks:
import React, { useEffect } from "react";
function VerifyEmail({ verifyToken }) {
const [ verifyEmail, { loading, data, error }] = useMutation(VERIFY_EMAIL_MUTATION);
useEffect(() => {
verifyEmail({
variables: { verifyToken },
});
}, []);
return (
<>
{loading && <p>Loading...</p>}
{data && <p>Verified successfully!</p>}
{error && <p>Error!</p>}
</>
)
}
If you somehow want to keep using classes, the only solution is to create a component and utilise componentDidMount of the component for this purpose.
// Current component:
<Mutation mutation={VERIFY_EMAIL_MUTATION} variables={{ verifyToken }}>
{verifyEmail => (
<SendEmail token={verifyToken} verify={verifyEmail} />
)}
</Mutation>
// Send Email component
class SendEmail extends Component {
componentDidMount() {
const { token, verify } = this.props;
verify(token);
}
render() {
return (
//handle loading, data and error states
)
}
}
I am trying to implement email verification system in react-apollo application and running into an issue. The problem is that I want to fire a GraphQL mutation on page load when a user visits a link with a verification token. The mutation currently is fired on a button click, but I want it to happen on page load.
I tried to return the mutation from render but it sent the application into an infinite loop.
return (
<Mutation
mutation={VERIFY_EMAIL_MUTATION}
variables={{ id }}
onCompleted={() => this.setState({ userVerified: true })}
>
{(verifyEmail, { loading, error }) => {
verifyEmail();
}
</Mutation>
How can I implement firing this mutation on page load?
Use compose and pass it down as a function to your component. The following method allows you to pass multiple mutations/queries, you can use them where-ever you want without and with triggers.
import React from "react";
import { compose, graphql } from "react-apollo";
import {
VERIFY_EMAIL_MUTATION
} from "../GraphqlQueries/ServerQueries";
class YourComponent extends React.Component {
componentWillMount() {
this.props.verifyEmail({variables: {email: "your_email", variable2: "variable2" }});
}
render() {
return (
<React.Fragment>
Your Render
</React.Fragment>
);
}
}
export default compose(
graphql(VERIFY_EMAIL_MUTATION, {
name: "verifyEmail"
})
)(YourComponent);
Rather than compose you can do it like this
useEffect(() => {
MutationName({
variables: {
variable1: variable1Value
}
});
}, []);
useEffect behave as on pageload.
I want to know what's the best way to handle setting a parent's state when the Apollo <Query> component finishes loading? I have an id that I sometimes have to query for. I wonder what's the best way to handle this case?
Currently I have it where the child component will listen for prop changes and if I notice that the prop for the data I'm looking for changes I'll call a function to update the state.
Is there a better way to handle this without needing the child component to listen to updates?
This is a pseudo code of what I'm currently doing
import * as React from 'react';
import { Query } from 'react-apollo';
class FolderFetcher extends React.Component<Props, { id: ?string}> {
constructor(props) {
super(props);
this.state = {
id: props.id
}
}
setId = (id) => {
this.setState({ id });
};
render() {
const { id } = this.props;
return (
<Query skip={...} query={...}>
((data) => {
<ChildComponent id={id} newId={data.id} setId={this.setId} />
})
</Query>
);
}
}
class ChildComponent extends React.Component<Props> {
componentDidUpdate(prevProps) {
if (prevProps.newId !== this.props.newId &&
this.props.id !== this.props.newId) {
this.props.setId(this.props.newId);
}
}
render() {
...
}
}
you can export child as wrapped with HoC from apollo
import { graphql } from 'react-apollo'
class ChildComponent extends React.Component {
// inside props you have now handy `this.props.data` where you can check for `this.props.data.loading == true | false`, initially it's true, so when you will assert for false you have check if the loading was finished.
}
export default graphql(Query)(ChildComponent)
Another option would be to get client manually, and run client.query() which will return promise and you can chain for the then() method.