Reputation: 51
I've followed the Recoil docs to create the attached which works but with the following warning
Warning: Can't perform a React state update on a component that hasn't mounted yet. This indicates that you have a side-effect in your render function that asynchronously later calls tries to update the component. Move this work to useEffect instead
I can't see any information about this in the docs. Can someone please explain what I need to change?
Thank you!
selector and component screen shot
Code selector.js…
import { gql } from "@apollo/client";
import { atom, selector } from "recoil";
import client from "../apollo-client";
export const stateQuery = selector({
key: "siteState",
get: async ({ get }) => {
const response = await client.query({
query: gql`
{
siteSettings {
siteSettings {
contactEmail
internationalTelephone
}
}
}
`,
});
return {
contactEmail: response.data.siteSettings.siteSettings.contactEmail,
internationalTelephone:
response.data.siteSettings.siteSettings.internationalTelephone,
};
},
});
Code ExampleBlock.js…
import React, { useState, useEffect } from "react";
import { useRecoilValue } from "recoil";
import { stateQuery } from "@recoil/selector";
import tw from "twin.macro";
export default function ArticleQuoteBlock({ data: { text, name } }) {
const { contactEmail, internationalTelephone } = useRecoilValue(stateQuery);
return (
<section tw="bg-white quote md:quote-md">
<div tw="bg-pink quote-inner md:quote-inner-md">
<h1 tw="quote-text md:quote-text-md">{contactEmail}</h1>
<h1 tw="quote-text md:quote-text-md">{internationalTelephone}</h1>
<h1 tw="quote-text md:quote-text-md"></h1>
{text && (
<p tw="quote-text md:quote-text-md indent md:indent-md">{text}</p>
)}
{name && <p tw="name md:name-md">{name}</p>}
</div>
</section>
);
}
Upvotes: 3
Views: 2298
Reputation: 434
I believe, your ExampleBlock.js tries to mount contactEmail and internationalTelephone before their values are available. You can utilize useState and useEffect hooks to solve the issue. useState can be used to trigger rendering when the data is updated. useEffect can be used to wait for component to mount. Since you have an asynchronous call, you should define an asynchronous function inside of the useEffect, then trigger it, so that you can wait for the result and update your states accordingly.
Simplest useState usage:
const [yourState, setYourState] = useState('initialValue')
. Initial value can be anything i.e. int, string, array etc.
Simplest useEffect usage:
useEffect(()=> { some_func() },[some_arg])
. When some_arg
is empty, some_func()
is triggered only once.
In your case, below code snippet might solve the issue.
export default function ArticleQuoteBlock({ data: { text, name } }) {
const [contactEmailInfo, setContactEmailInfo] = useState();
const [internationalTelephoneInfo, setInternationalTelephoneInfo] = useState();
useEffect(() => {
async function fetchData() {
let contactEmail;
let internationalTelephone;
({ contactEmail, internationalTelephone } = await useRecoilValue(stateQuery))
setContactEmailInfo(contactEmail);
setInternationalTelephoneInfo(internationalTelephone);
}
fetchData()
}, []);
return (
<section tw="bg-white quote md:quote-md">
<div tw="bg-pink quote-inner md:quote-inner-md">
<h1 tw="quote-text md:quote-text-md">{contactEmailInfo}</h1>
<h1 tw="quote-text md:quote-text-md">{internationalTelephoneInfo}</h1>
<h1 tw="quote-text md:quote-text-md"></h1>
{text && <p tw="quote-text md:quote-text-md indent md:indent-md">{text}</p>}
{name && <p tw="name md:name-md">{name}</p>}
</div>
</section>
);
}
Upvotes: 1