Я столкнулся с проблемой с пакетом react-apollo
:
- У меня есть 2 компонента, которые запрашивают один и тот же объект graphql
- Каждый компонент работает независимо, без ошибок
- Когда я отрисовываю оба, я получаю ошибку
Cannot read property of undefined
Я смог воссоздать ошибку с помощью этого демонстрационного кода.Пожалуйста, найдите его здесь, а также ниже: https://codesandbox.io/s/qvq0y25384
То, что я пробовал без успеха
- Использование псевдонимов запроса привело к точно такому же поведению
- Использование
fetchPolicy="no-cache"
сработало, но это не приемлемо для моего реального случая использования, когда 2 компонента должны использовать кэш для повышения производительности. - Использование sharedфрагменты сработали, но это неприемлемо для моего реального случая использования, когда это замедляет оба компонента на каждой странице
Ошибка, которая возникает только при визуализации 2 компонентов
PlanetPhysics.js? [sm]:27 Uncaught TypeError: Cannot read property 'planets' of undefined
at eval (PlanetPhysics.js? [sm]:27)
at Query.render (react-apollo.browser.umd.js:479)
at finishClassComponent (react-dom.development.js:13194)
at updateClassComponent (react-dom.development.js:13156)
at beginWork (react-dom.development.js:13825)
at performUnitOfWork (react-dom.development.js:15864)
at workLoop (react-dom.development.js:15903)
at HTMLUnknownElement.callCallback (react-dom.development.js:100)
at Object.invokeGuardedCallbackDev (react-dom.development.js:138)
at invokeGuardedCallback (react-dom.development.js:187)
at replayUnitOfWork (react-dom.development.js:15311)
at renderRoot (react-dom.development.js:15963)
at performWorkOnRoot (react-dom.development.js:16561)
at performWork (react-dom.development.js:16483)
at performSyncWork (react-dom.development.js:16455)
at requestWork (react-dom.development.js:16355)
at scheduleWork$1 (react-dom.development.js:16219)
at Object.enqueueForceUpdate (react-dom.development.js:11337)
at Query.Component.forceUpdate (react.development.js:288)
at Query._this.updateCurrentData (react-apollo.browser.umd.js:371)
at Object.next (react-apollo.browser.umd.js:342)
at notifySubscription (Observable.js:126)
at onNotify (Observable.js:161)
at SubscriptionObserver.next (Observable.js:215)
at eval (bundle.umd.js:429)
at Array.forEach (<anonymous>)
at Object.next (bundle.umd.js:429)
at eval (bundle.umd.js:1123)
at eval (bundle.umd.js:1414)
at Array.forEach (<anonymous>)
at eval (bundle.umd.js:1413)
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (bundle.umd.js:1408)
at Object.next (bundle.umd.js:1465)
at notifySubscription (Observable.js:126)
at onNotify (Observable.js:161)
at SubscriptionObserver.next (Observable.js:215)
at eval (bundle.umd.js:62)
at Array.forEach (<anonymous>)
at Object.next (bundle.umd.js:62)
at notifySubscription (Observable.js:126)
at onNotify (Observable.js:161)
at SubscriptionObserver.next (Observable.js:215)
at eval (bundle.umd.js:95)
The above error occurred in the <Query> component:
in Query (created by PlanetPhysics)
in PlanetPhysics (created by AppERROR1)
in ApolloProvider (created by AppERROR1)
in AppERROR1
Полный код для воссоздания ошибки
index.js
import React from "react";
import ReactDOM from "react-dom";
import { ApolloProvider } from "react-apollo";
import { createClient } from "./client";
import { PlanetPhysics } from "./PlanetPhysics";
import { PlanetDemographics } from "./PlanetDemographics";
const client = createClient({});
/* The error only happens when both components are rendered
* Uncaught TypeError: Cannot read property 'planets' of undefined
*/
function AppOK1() {
return (
<ApolloProvider client={client}>
<PlanetPhysics />
</ApolloProvider>
);
}
function AppOK2() {
return (
<ApolloProvider client={client}>
<PlanetDemographics />
</ApolloProvider>
);
}
function AppERROR1() {
return (
<ApolloProvider client={client}>
<PlanetPhysics />
<PlanetDemographics />
</ApolloProvider>
);
}
function AppERROR2() {
return (
<ApolloProvider client={client}>
<PlanetDemographics />
<PlanetPhysics />
</ApolloProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<AppOK1 />, rootElement);
// ReactDOM.render(<AppOK2 />, rootElement);
// ReactDOM.render(<AppERROR1 />, rootElement);
// ReactDOM.render(<AppERROR2 />, rootElement);
client.js
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
export function createClient(initialState) {
return new ApolloClient({
connectToDevTools: true,
ssrMode: false,
link: new HttpLink({
uri: "https://prevostc-swapi-graphql.herokuapp.com"
}),
cache: new InMemoryCache({
dataIdFromObject: object => {
const identifier = object.uuid || JSON.stringify(object);
return object.__typename + "-" + identifier || null;
}
}).restore(initialState || {})
});
}
PlanetPhysics.js
import React from "react";
import gql from "graphql-tag";
import { Query } from "react-apollo";
import { DataBlock } from "./DataBlock";
const PHYSICS_QUERY = gql`
query {
allPlanets(first: 4) {
planets {
id
name
diameter
rotationPeriod
}
}
}
`;
export function PlanetPhysics() {
return (
<Query query={PHYSICS_QUERY}>
{({ loading, error, data }) => {
if (loading) return "Loading...";
if (error) return `Error! ${error.message}`;
return (
<div>
{data.allPlanets.planets.map(p => (
<DataBlock data={p} title={`Physics: ${p.name}`} key={p.id} />
))}
</div>
);
}}
</Query>
);
}
PlanetDemographics.js
import React from "react";
import gql from "graphql-tag";
import { Query } from "react-apollo";
import { DataBlock } from "./DataBlock";
const DEMOGRAPHICS_QUERY = gql`
query {
allPlanets(first: 4) {
planets {
id
name
population
}
}
}
`;
export function PlanetDemographics() {
return (
<Query query={DEMOGRAPHICS_QUERY}>
{({ loading, error, data }) => {
if (loading) return "Loading...";
if (error) return `Error! ${error.message}`;
return (
<div>
{data.allPlanets.planets.map(p => (
<DataBlock
data={p}
title={`Demographics: ${p.name}`}
key={p.id}
/>
))}
</div>
);
}}
</Query>
);
}
DataBlock.js
import React from "react";
export function DataBlock({ title, data }) {
return (
<div
style={{
border: "1px solid black",
borderRadius: "5px",
marginBottom: "10px",
marginRight: "10px",
padding: "10px",
maxWidth: "220px",
display: "inline-block"
}}
>
<h3 style={{ margin: "5px 5px", textAlign: "center" }}>{title}</h3>
<table>
<tbody>
{Object.keys(data).map(key => (
<tr key={key}>
<td style={{ textAlign: "right" }}>{key}: </td>
<td>{data[key]}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}