Loading __typename fields in seperate queries Apollo-Client not Updating UI - graphql

I'm attempting to load information about a type in 2 steps (since the info I'm asking for in secondQuery will take some time to load):
Component:
const MyComponent = ({startDate}) => {
const firstQuery = useQuery(
GET_INFO_PART_ONE,
{
variables: {startDate}
}
);
const secondQuery = useQuery(
GET_INFO_PART_TWO,
{
variables: {startDate}
}
);
}
Queries:
export const GET_INFO_PART_ONE = gql`
query getInfoPartOne(
$startDate: DateTime!
) {
infoPageResults(
startDate: $startDate
) {
edges {
info {
infoID
infoFieldOne
infoFieldTwo
infoFieldThree
}
}
}
}
`;
export const GET_INFO_PART_TWO = gql`
query getInfoPartTwo(
$startDate: DateTime!
) {
infoPageResults(
startDate: $startDate
) {
edges {
info {
infoID
infoFieldFour{
netRate
}
}
}
}
}
`;
When I do this and both queries resolve, the cache's ROOT_QUERY includes the data as I would expect it, with infoPageResults containing an array of edges where each edge's info __typename includes the fields specified in the GET_INFO_PART_ONE and GET_INFO_PART_TWO queries. I would then expect firstQuery.data.infoPageResults.edges in the above component to include the fields loaded from the second query.
The Problem
After both firstQuery and secondQuery are finished loading, firstQuery.data.infoPageResults.edges does not contain the fields loaded by secondQuery, even though the cached values look as I would expect them.
Is there something obvious I'm misunderstanding about how the query hooks work?
Is there a better strategy for loading additional fields onto a _typename in 2 steps?

Just caches what was queried ... no node entries cached (additionally, separately - as cache is normalizing cache), because no id field (required by default as unique entry id) ... it can be customized to use infoID for info type (see docs).
Properly cached node (info) entries can be used to display node details (from 1st query) on 2nd query result rendering list ... using additional 'cache-only' policy queries (info node by id) in subcomponents (react).
Start with more standard example/tutorial to explore possibilities.
update
No, data fetched separately are accessible separately ... it's not cache problem and not querying problem...
1st query won't return fields/properties fetched in 2nd query ... by design;
list rendered using 1st result won't be refreshed when 2nd query result will arrive ... if doesn't contain ref/not consume 2nd data;
You can use/pass both data in parallel or combine them (in immutable way) before passing to some view component ('Presentational and Container Component Pattern') ... list can be rendered using one prop and updated when 2nd prop changes.

Related

How to query relational data in ascending order in strapi?

I have this query that works
async find(ctx) {
let { _start, _limit } = ctx.request.query;
console.log(ctx.request.query)
_limit ? 0 : (_limit = 10);
const entities = await strapi.services["course-series"].find({});
return entities.map((entity) => {
// Do I sort them here or in the url query (and how)
entity.courses = entity.courses.slice(_start, _limit);
return sanitizeEntity(entity, { model: strapi.models["course-series"] });
});
}
The idea is that I can load 10 courses from each series at first and then get the next 10...
I just realized that the first 10 I am getting are not the recent ones.
As I commented // Do I sort them here or in the url query (and how)
What version of Strapi do you use?
What does this line do strapi.services["course-series"].find({})? How did you build this find method in the service? What does it do? Does it accept params?
Personally I'd do something like that (assuming you're working with Strapi version > 4:
const entities = await strapi.entityService.findMany('api::course-series.course-series', {
fields: [/* list the course-series fields you want to populate */],
populate: {
courses: {
fields: [/* list the course fields you want to populate */],
sort: 'createdAt:desc', // You can use id, publishedAt or updatedAt here, depends on your sorting prefrences
offset: _start,
limit: _limit // I must admit I haven't tested `offset` and `limit` on the populated related field
}
}
})
// ...the rest of your code, if needed
Read more about Entity Service API here.
Doing it the way you did it, you will always first retrieve the full list of courses for each course-series, and then run costly operations like mapping (the lesser of 2 evils) and above all sorting.

Contentful GraphQL filter with relational queries and reference fields

I'm currently trying to filter entries with a field references, many.
In other words, the field takes multiple locations with each locations consisting of city among other fields.
For my query I want to get every entry that has a location with a specific city name set.
The following query filters an entry that takes only on reference for location, how would it look like when having many?
export const NEWS_ARTICLES = gql`
query GetNewsArticles($location: String) {
entryCollection(
where: {
location: { city: $location }
}
) {
items {
location
}
}
}
`;
Pulling GraphQL's schema all I get is locationCollection_exists - is this even possible?
I found the following article, which seems to do what I desire, but for the Filter API and not GraphQL: https://www.contentful.com/developers/docs/concepts/relational-queries/
Contentful DevRel here. 👋
Filtering by a linked collection entry is not supported right now. What you can do though it flip the query around.
export const NEWS_ARTICLES = gql`
query GetNewsArticles($location: String) {
cityCollection(
# your filter
) {
items {
linkedFrom {
# all the entries linking to this entry
}
}
}
}
`;
You can find more information about this approach in the docs or this blogpost.

How to perform a nested query in graphql using gatsby-source-prismic

I'm just getting started with gatsby and graphql and I've started using prismic as a cms. I cannot figure out how to perform nested queries, avoiding overfetching. I don't even know if it would be possible or if it's more just me thinking of the problem in SQL terms.
I have two custom types on prismic which are related using a content relationship. These are Productions which have many People through a repeatable group of fields. The result I want is to have a page (the home) which shows the latest production with the list of people who starred in it, and another page with each person from people and all their roles in every production.
I managed to do this by fetching all the people in the home page and the required production and filtering the returned data in the frontend via javascript. However, I really feel that this way is not ideal since it requires to fetch all the people and not only the ones required for my page.
allPrismicProduction(
limit: 1
sort: { fields: [last_publication_date], order: DESC }
) {
edges {
node {
data {
casting {
...castingData
}
}
}
}
}
allPrismicPerson {
edges {
node {
id
data {
name {
text
}
photo {
url
}
}
}
}
}
}
const productions = data.allPrismicProduction.edges
const people = data.allPrismicPerson.edges
const sortedprods = []
productions.forEach(el => {
let castings = el.node.data.casting.map(cast => cast.person.uid)
castings.forEach(casting =>{
people.filter(person => {
if(castings.indexOf(person.node.uid) > -1){
return person
}
sortedprods.push({
production: el.node,
people: relpeople,
})
})
})
So what I do is I fetch all people and then filter them according to the uids found in the productions returned by the query.
I would like to know if it is possible, or otherwise what would be a better way to achieve this, how to limit overfetching by making it possible to only fetch the people who's uid is present in the productions given by the first part of the query.
Is this way of thinking compatible with graphql?
I managed to solve this by looking around a bit more in the other issues of gatsby-source-prismic on github.
The related-content node can be queried using the following structure:
{
allPrismicMyContentType {
edges {
node {
data {
my_relations {
relation {
document {
data {
where in data you can access all the properties of the type needed.
In this way one can do a single query to get all related content on prismic

GraphQL queries for Gatsby - Contentful setup with a flexible content model

I have a gatsby site with the contentful plugin and graphql queries (setup is working).
[EDIT]
My gatsby setup pulls data dynamically using the pageCreate feature. And populates my template component, the root graphql query of which I've shared below. I can create multiple pages using the setup if the pages on contentful follow the structure given in the below query.
[/EDIT]
My question is about a limitation I seemed to have come across or just don't know enough grpahql to understand this yet.
My high level content model 'BasicPageLayout' consists of references to other content types through the field 'Section'. So, it's flexible in terms of which content types are contained in the 'BasicPageLayout' and the order in which they are added.
Root page query
export const pageQuery = graphql`
query basicPageQuery {
contentfulBasicPageLayout(pageName: {eq: "Home"}) {
heroSection {
parent {
id
}
...HeroFields
}
section1 {
parent {
id
}
...ContentText
}
section2 {
parent {
id
}
...ContentTextOverMedia
}
section3 {
parent {
id
}
...ContentTextAndImage
}
section4 {
parent {
id
}
...ContentText
}
}
}
The content type fragments all live in the respecitve UI components.
The above query and setup are working.
Now, I have "Home" Hard coded because I'm having trouble creating a flexible reusable query. I'm taking advantage of contentful's flexible nature when creating the models, but haven't found a way to create that flexibility in the graphql query for it.
What I do know:
Graphql query is resolved at run time, so everything that needs to be fetched should be in that query. It can't be 'dynamic'.
Issue: The 'Section' fields in the basicPageLayout can link to any content type. So we can mix and match the granular level content types. How do I add the content type fragment (like ContentTextAndImage vs ContentText) so it is appropriate for that section instance ('Section' field in the query)?
In other words
I'd like the root query to get 'Home' data which might have 4 sections, all of type - ContentTextOverMedia
as well as 'About ' data that might have also have 4 sections but with alternating types - ContentText and ContentTextAndImage
This is the goal because I want to create content (Pages) by mix-matching content types on contentful, without needing to update the code each time a new Page is created. Which is why Contentful is useful and was picked in the first place.
My ideas so far:
A. Run two queries, in series. One fetches the parent.id on each section and that holds the content type info. Second fetches the data using the appropriate fragment.
B. Fetch the JSON file of the basicPageLayouts content instance (such as 'Home') separately through Contentful API, and using that JSON file create the graphql string to be used in each instance (So, different layout for Home, About, and so on)
This needs more experimentation, not sure if it's viable, could also be more complex then it needs to be.
So, please share thoughts on the above paths that I'm exploring or another solution that I haven't considered using graphql or gatsby's features.
This is my first question on SO btw, I've spent some time on refining it and trying to follow the guidelines but please do give me feedback in comments so I can improve even if you don't have an answer to my question.
Thanks in advance.
If I understood correctly you want to create pages dynamically from the data coming from Contentful.
You can achieve this using the Gatsbyjs Node API specifically createPage.
In your gatsby-node.js file you can have something like this
const fs = require('fs-extra')
const path = require('path')
exports.createPages = ({graphql, boundActionCreators}) => {
const {createPage} = boundActionCreators
return new Promise((resolve, reject) => {
const landingPageTemplate = path.resolve('src/templates/landing-page.js')
resolve(
graphql(`
{
allContentfulBesicPageLayout {
edges {
node {
pageName
}
}
}
}
`).then((result) => {
if (result.errors) {
reject(result.errors)
}
result.data.allContentfulBesicPageLayout.edges.forEach((edge) => {
createPage ({
path: `${edge.node.pageName}`,
component: landingPageTemplate,
context: {
slug: edge.node.pageName // this will passed to each page gatsby create
}
})
})
return
})
)
})
}
Now in your src/templates/landing-page.js
import React, { Component } from 'react'
const LandingPage = ({data}) => {
return (<div>Add you html here</div>)
}
export const pageQuery = graphql`
query basicPageQuery($pageName: String!) {
contentfulBasicPageLayout(pageName: {eq: $pageName}) {
heroSection {
parent {
id
}
...HeroFields
}
section1 {
parent {
id
}
...ContentText
}
section2 {
parent {
id
}
...ContentTextOverMedia
}
section3 {
parent {
id
}
...ContentTextAndImage
}
section4 {
parent {
id
}
...ContentText
}
}
}
note the $pageName param that's what was passed to the component context when creating a page.
This way you will end up creating as many pages as you want.
Please note: the react part of the code was not tested but I hope you get the idea.
Update:
To have a flexible query you instead of having your content Types as single ref field, you can have one field called sections and you can add the section you want there in the order you desire.
Your query will look like this
export const pageQuery = graphql`
query basicPageQuery($pageName: String!) {
contentfulBasicPageLayout(pageName: {eq: $pageName}) {
sections {
... on ContentfulHeroFields {
internal {
type
}
}
}
}
Khaled

Ordered list of redux-form fields

Do you know how can I get the ordered list of field names from given form? Instance API has a property called "fieldList" and it's an array but it's not in correct order. (ordered list = [firstFieldName, secondFieldName, ...] so what I need is a list of field names in order they appear in my form - top to bottom)
Also the redux-form' action '##redux-form/REGISTER_FIELD' is dispatching out of correct form order so I guess it's not what I need here...
(My redux-form version: 7.3.0)
I have experience with redux-form and also have checked its API, but didn't find a documented way for getting the fields in the way they appear in the form.
However, here's how I would do it:
I'll create a Reducer, that will keep track of the fields in the order,
they are registered (appear in the form).
We have very detailed action. As you already mentioned - ##redux-form/REGISTER_FIELD action is dispatching out all the fields in process of being registered in the correct order. This action has the following payload:
{
type: '##redux-form/REGISTER_FIELD',
meta: {
form: 'user'
},
payload: {
name: 'firstName',
type: 'Field'
}
}
Create a reducer. So I'll just create a Reducer, that will listen for all ##redux-form/REGISTER_FIELD actions. Something like that:
// The reducer will keep track of all fields in the order they are registered by a form.
// For example: `user` form has two registered fields `firstName, lastName`:
// { user: ['firstName', 'lastName'] }
const formEnhancedReducer = (state = {}, action) {
switch (action.type) {
case '##redux-form/REGISTER_FIELD':
const form = action.meta.form
const field = action.payload.name
return { ...state, [form]: [...state[form], field] }
default:
return state
}
}
Usage. In order to get ordered fields by a form, you just have access the Store (state) formEnhancer property with the form name: state.formEnhanced.user.
Keep in mind that you have to consider some cases as ##redux-form/DESTROY, but I think it's a pretty straightforward implementation.
I would prefer to keep things simple and just subscribed to ##redux-form/REGISTER_FIELD and just change the reducer implementation a little bit, in order to prevent form fields duplication. So I will just validate if the form field is already registered and won't care for supporting ##redux-form/DESTROY.
Hope it helps.
One way that I have been able to retrieve an ordered list of form field names from a given form is via the registered fields stored in the redux form state using the connect HOC (Higher Order Component) from 'react-redux':
import React, { Component } from 'react';
import { connect } from 'react-redux';
import _ from 'lodash';
class Foo extends Component {
render() {
const {
registeredFields,
} = this.props;
...
...
...
}
}
const mapStateToProps = (state, props) => {
// retrieve the registered fields from the form that is stored in redux state; using lodash 'get' function
const registeredFields = _.get(state, 'form.nameOfYourForm.registeredFields');
// creating an object with the field name as the key and the position as the value
const registeredFieldPositions = _.chain(registeredFields).keys().reduce((registeredFieldPositions, key, index) => {
registeredFieldPositions[key] = index;
return registeredFieldPositions;
}, {}).value();
return({
registeredFieldPositions,
});
};
// registeredFieldPositions will now be passed as a prop to Foo
export default connect(mapStateToProps)(Foo);

Resources