Je travaille sur une application react avec react-apollo Je n'ai pas d'autre choix que d'appeler les données à travers graphql lorsque je vérifie dans le navigateur la réponse de l'onglet réseau qui montre tous les éléments du tableau différents. mais ce que j'obtiens ou console.log()
Dans mon application, tous les éléments du tableau sont identiques au premier élément. Je ne sais pas comment résoudre ce problème.
Réponses
Trop de publicités?La raison pour laquelle cela se produit est que les éléments de votre tableau sont "normalisés" aux mêmes valeurs dans le cache d'Apollo. Autrement dit, ils ont la même apparence pour Apollo. Cela se produit généralement parce qu'ils partagent le même Symbol(id)
.
Si vous imprimez votre objet de réponse Apollo, vous remarquerez que chacun des objets a Symbol(id)
qui est utilisé par le cache Apollo. Les éléments de votre tableau ont probablement le même Symbol(id)
ce qui les fait se répéter. Pourquoi cela se produit-il ?
Par défaut, le cache d'Apollo fonctionne comme suit fonction pour la normalisation.
export function defaultDataIdFromObject(result: any): string | null {
if (result.__typename) {
if (result.id !== undefined) {
return `${result.__typename}:${result.id}`;
}
if (result._id !== undefined) {
return `${result.__typename}:${result._id}`;
}
}
return null;
}
Les propriétés des éléments de votre tableau font que plusieurs éléments renvoient le même identifiant de données. Dans mon cas, plusieurs éléments avaient _id = null
ce qui a entraîné la répétition de tous ces éléments. Lorsque cette fonction renvoie un résultat nul, le documents dire
InMemoryCache reviendra au chemin d'accès à l'objet dans la requête, comme ROOT_QUERY.allPeople.0 pour le premier enregistrement renvoyé par la requête Root allPeople Root query.
C'est le comportement que nous voulons lorsque les éléments de notre tableau ne fonctionnent pas bien avec defaultDataIdFromObject
.
La solution consiste donc à configurer manuellement ces identifiants uniques à l'aide de l'option dataIdFromObject
transmise à l'option InMemoryCache
dans le constructeur de votre ApolloClient
. Ce qui suit a fonctionné pour moi car tous mes objets utilisent _id et ont __typename.
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
dataIdFromObject: o => (o._id ? `${o.__typename}:${o._id}`: null),
})
});
Je pense que l'approche décrite dans les deux autres réponses devrait être évitée au profit de l'approche suivante :
En fait, c'est très simple. Pour comprendre comment cela fonctionne, il suffit d'enregistrer obj comme suit :
dataIdFromObject: (obj) => {
let id = defaultDataIdFromObject(obj);
console.log('defaultDataIdFromObject OBJ ID', obj, id);
Vous verrez que l'identifiant sera nul dans vos journaux si vous avez ce problème.
Prêtez attention à la mention "obj". Il sera imprimé pour chaque objet renvoyé.
Ces objets sont ceux à partir desquels Apollo essaie d'obtenir un identifiant unique (vous devez indiquer à Apollo quel champ de votre objet est unique pour chaque objet de votre tableau d'"items" retourné par GraphQL - de la même manière que vous passez une valeur unique pour "key" dans React lorsque vous utilisez "map" ou d'autres itérations lors du rendu des éléments du DOM).
Par défaut, InMemoryCache tente d'utiliser les clés primaires les plus courantes. clés primaires id et _id pour l'identifiant unique, si elles existent. ainsi que __typename sur un objet.
Regardez donc l'objet enregistré utilisé par "defaultDataIdFromObject" - si vous ne voyez pas "id" ou "_id", vous devez fournir le champ de votre objet qui est unique pour chaque objet.
J'ai modifié l'exemple d'Apollo dox pour couvrir trois cas où vous avez pu fournir des identifiants incorrects - il s'agit des cas où vous avez plus d'un type GraphQL :
dataIdFromObject: (obj) => {
let id = defaultDataIdFromObject(obj);
console.log('defaultDataIdFromObject OBJ ID', obj, id);
if (!id) {
const { __typename: typename } = obj;
switch (typename) {
case 'Blog':
// if you are using other than 'id' and '_id' - 'blogId' in this case
const undef = `${typename}:${obj.id}`;
const defined = `${typename}:${obj.blogId}`;
console.log('in Blogs -', undef, defined);
return `${typename}:${obj.blogId}`; // return 'blogId' as it is a unique
//identifier. Using any other identifier will lead to above defined problem.
case 'Post':
// if you are using hash key and sort key then hash key is not unique.
// If you do query in DB it will always be the same.
// If you do scan in DB quite often it will be the same value.
// So use both hash key and sort key instead to avoid the problem.
// Using both ensures ID used by Apollo is always unique.
// If for post you are using hashKey of blogID and sortKey of postId
const notUniq = `${typename}:${obj.blogId}`;
const notUniq2 = `${typename}:${obj.postId}`;
const uniq = `${typename}:${obj.blogId}${obj.postId}`;
console.log('in Post -', notUniq, notUniq2, uniq);
return `${typename}:${obj.blogId}${obj.postId}`;
case 'Comment':
// lets assume 'comment's identifier is 'id'
// but you dont use it in your app and do not fetch from GraphQl, that is
// you omitted 'id' in your GraphQL query definition.
const undefnd = `${typename}:${obj.id}`;
console.log('in Comment -', undefnd);
// log result - null
// to fix it simply add 'id' in your GraphQL definition.
return `${typename}:${obj.id}`;
default:
console.log('one falling to default-not good-define this in separate Case', ${typename});
return id;
}
}
return id;
}
J'espère que vous comprenez maintenant que l'approche des deux autres réponses est risquée.
VOUS DISPOSEZ TOUJOURS D'UN IDENTIFIANT UNIQUE. IL SUFFIT D'AIDER APOLLO EN LUI INDIQUANT DE QUEL CHAMP DE L'OBJET IL S'AGIT. S'il n'est pas récupéré en l'ajoutant dans la définition de la requête, ajoutez-le.