I'm currently migrating my Gatsby V3 to Gatsby V4.
I have a createNode using a function that retrieve some data from GraphQL using getNodesByType("myType").
function getSecteur(realisation) {
const secteurs = getNodesByType(`StrapiSecteurs`);
console.log(secteurs)
if (realisation.client && realisation.client.secteur) {
return secteurs.find(
(secteur) => secteur.strapiId === realisation.client.secteur
);
}
exports.onCreateNode = async ({
node,
actions,
getNodesByType,
}) => {
const { createNode } = actions;
if (node.internal.type === "StrapiRealisations") {
createNode({
...node,
id: `${node.id}-markdown`,
parent: node.id,
children: [],
client: {
...node.client,
secteur: getSecteur(node),
},
citation: getCitation(node),
internal: {
type: "MDRealisation",
contentDigest: digest(node),
},
});
}
But in V4 this function does not retrieve any data, but I cannot found an alternative for getNodesByType.
Any idea(s) ?
Thanks
Have you tried something like:
exports.sourceNodes = ({ actions, getNodesByType }) => {
getNodesByType("StrapiSecteurs").forEach(node => actions.touchNode(node))
}
Related
I created a hook
export function useRedirectStartParams() {
const scenarios: IScenario[] = useSelector(scenariosSelector);
const block: IBlockItem = useSelector(selectedBlockSelector);
const [redirects, setRedirects] = useState<IDropdownEl[]>([]);
useEffect(() => {
const newRedirects =
block?.block_data?.redirects?.map((block: any) => {
const scenarioName = scenarios.find(
(scenario) => scenario.id === block.scenario_id
)?.name;
return {
name: scenarioName,
val: {
scenarioId: block.scenario_id,
blockId: block.id,
},
};
}) || [];
setRedirects(newRedirects);
}, [block, scenarios]);
return { redirects };
}
use it in my component
const { redirects } = useRedirectStartParams();
and then try to test it with jest like this
import { useRedirectStartParams } from "#hooks/useRedirectStartParams";
jest.mock("#hooks/useRedirectStartParams");
beforeEach(() => {
useRedirectStartParams.mockReturnValueOnce({
redirects: [{ name: "ad", val: "123" }],
});
})
but got error that redirects are undefined.
I also tried to mock hook this way
jest.mock("#hooks/useRedirectStartParams", () => jest.fn());
And it didn't help me
I expect that redirects won't be undefined. Not renderHook. i want to mock value of hook for component
I've got a problem in a next.js typescript project where i'm using zustand store with persist and immer: https://github.com/pmndrs/zustand
If the toggle_slide_over function is outside of the session_setup object, it works.
If it is inside, it throws runtime error 'not a function'.
Why & how do i fix this?
Interface:
export interface MyAppState {
session_setup: {
slide_over_open: boolean,
toggle_slide_over: (new_toggle_state: boolean) => void,
...
},
session_setup_toggle_slide_over: (new_toggle_state: boolean) => void,
...
}
Function declarations inside doPersist():
const doPersist: () any => { return persist((set) => ({
session_setup: {
...
toggle_slide_over: (new_toggle_state) => set(
produce(state => { state.session_setup.slide_over_open = new_toggle_state })
),
},
session_setup_toggle_slide_over: (new_toggle_state) => set(
produce(state => { state.session_setup.slide_over_open = new_toggle_state })
),
...
How they are retrieved in React:
// When nested:
const toggle_slide_over = useMyAppStore(state => state.session_setup.toggle_slide_over)
// When not nested:
const toggle_slide_over = useMyAppStore(state => state.session_setup_toggle_slide_over)
How they are used:
onClick={() => toggle_slide_over(new_state)}
Store:
const initializeStoreByEnv = (): any => {
if (process.env.NODE_ENV === 'development') return devtools(doPersist())
else return doPersist()
}
export const useMyAppStore = create<MyAppState>()(
initializeStoreByEnv()
)
I've searched for related zustand/immer/redux/functions nested in objects/function not a function at runtime errors but haven't found anything useful, yet...
I'm working around this by just prefixing out-of-object functions with 'session_setup_', which is ok atm, but will get xxxl names with another level of nesting objects.
I completely ran into the same issue. I had nested slices, containing data and functions, which i wanted to persist using zustand/persist. I ended up going with this following approach:
src/zustand/store/index.ts
export const useMyAppStore = create<MyAppState>()(
persist(
(...args) => ({
foo: createFooSlice(...args)
}),
{
name: "app-store",
merge: (persistedState, defaultState) => {
if (!persistedState || typeof persistedState !== "object") {
return defaultState;
}
let resultState: MyAppState = { ...defaultState };
const keys = Object.keys(defaultState) as (keyof MyAppState)[]
keys.forEach((key) => {
if (key in persistedState) {
//#ts-ignore // TypeScript currently don't recognize that key exists in localState
const state = persistedState[key]
if (!!state) {
resultState = { ...resultState, [key]: { ...defaultState[key], ...state }}
}
}
})
return resultState;
}
}
)
I am trying to generate pages of each of the category's data from contentful ...
but there have some concerns.
Success
I could have got the data from contentful ..such as (title, image, and so on ) on the home page(localhost:8000)
Fail
when I clicked the read more button, Card on (localhost:8000), and switched into another page (localhost:8000/blog/tech/(content that I created on contentful)) that has not succeeded and 404pages has appeared(the only slag has changed and appeared that I hoped to).
Error
warn The GraphQL query in the non-page component "C:/Users/taiga/Github/Gatsby-new-development-blog/my-blog/src/templates/blog.js" will not
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
If the failing component(s) is a regular component and not intended to be a page
component, you generally want to use a <StaticQuery> (https://gatsbyjs.org/docs/static-query)
instead of exporting a page query.
my-repo
enter link description here
gatsby.node.js
const path = require(`path`);
const makeRequest = (graphql, request) => new Promise((resolve, reject) => {
// Query for nodes to use in creating pages.
resolve(
graphql(request).then(result => {
if (result.errors) {
reject(result.errors)
}
return result;
})
)
});
// Implement the Gatsby API "createPages". This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
// Create pages for each blog.
const getBlog = makeRequest(graphql, `
{
allContentfulBlog (
sort: { fields: [createdAt], order: DESC }
)
edges {
node {
id
slug
}
}
}
}
`).then(result => {
result.data.allContentfulBlog.edges.forEach(({ node }) => {
createPage({
path: `blog/${node.slug}`,
component: path.resolve(`src/templates/blog.js`),
context: {
id: node.id,
},
})
})
});
const getTech = makeRequest(graphql,`
{
allContentfulBlog (
sort: { fields: [createdAt], order: DESC }
filter: {
categories: {elemMatch: {category: {eq: "tech"}}}
},)
{
edges {
node {
id
slug
}
}
}
}
`).then(result => {
const blogs = result.data.allContentfulBlog.edges
const blogsPerPage = 9
const numPages = Math.ceil(blogs.length / blogsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/category/tech` : `/category/tech/${i + 1}`,
component: path.resolve("./src/templates/tech.js"),
context: {
limit: blogsPerPage,
skip: i * blogsPerPage,
numPages,
currentPage: i + 1
},
})
})
});
return Promise.all([
getBlog,
getTech
])
};
Hy I'm using the Apollo Client with React. I query the posts with many different variables. So I have one post in different "caches". Now I want to delete a post. So I need to delete this specific post from all "caches".
const client = new ApolloClient({
link: errorLink.concat(authLink.concat(httpLink)),
cache: new InMemoryCache()
});
Postquery:
export const POSTS = gql`
query posts(
$after: String
$orderBy: PostOrderByInput
$tags: JSONObject
$search: String
$orderByTime: Int
) {
posts(
after: $after
orderBy: $orderBy
tags: $tags
search: $search
orderByTime: $orderByTime
) {
id
title
...
}
}
`;
I tried it with the cache.modify(), which is undefined in my mutation([https://www.apollographql.com/docs/react/caching/cache-interaction/#cachemodify][1])
const [deletePost] = useMutation(DELETE_POST, {
onError: (er) => {
console.log(er);
},
update(cache, data) {
console.log(cache.modify())//UNDEFINED!!!
cache.modify({
id: cache.identify(thread), //identify is UNDEFINED + what is thread
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
}
}
})
}
});
I also used the useApolloClient() with the same result.
THX for any help.
Instead of using cache.modify you can use cache.evict, which makes the code much shorter:
deletePost({
variables: { id },
update(cache) {
const normalizedId = cache.identify({ id, __typename: 'Post' });
cache.evict({ id: normalizedId });
cache.gc();
}
});
this option worked for me
const GET_TASKS = gql`
query tasks($listID: String!) {
tasks(listID: $listID) {
_id
title
sort
}
}
`;
const REMOVE_TASK = gql`
mutation removeTask($_id: String) {
removeTask(_id: $_id) {
_id
}
}
`;
const Tasks = () => {
const { loading, error, data } = useQuery(GET_TASKS, {
variables: { listID: '1' },
});
сonst [removeTask] = useMutation(REMOVE_TASK);
const handleRemoveItem = _id => {
removeTask({
variables: { _id },
update(cache) {
cache.modify({
fields: {
tasks(existingTaskRefs, { readField }) {
return existingTaskRefs.filter(
taskRef => _id !== readField('_id', taskRef),
);
},
},
});
},
});
};
return (...);
};
You can pass your updater to the useMutation or to the deletePost. It should be easier with deletePost since it probably knows what it tries to delete:
deletePost({
variables: { idToRemove },
update(cache) {
cache.modify({
fields: {
posts(existingPosts = []) {
return existingPosts.filter(
postRef => idToRemove !== readField('id', postRef)
);
},
},
});
},
});
You should change variables to match your mutation. This should work since posts is at top level of your query. With deeper fields you'll need a way to get the id of the parent object. readQuery or a chain of readField from the top might help you with that.
So i want to subscribe to multiple events for the current logged user.
I've extracted the subscriptions to a separate function that update my logged user state from inside and returns an array of subscriptions.
Now i wanted to know is there a different / better way of doing this ?
Is this the correct / recommended way of approaching this problem ?
Current implementation
export const subscribeToCurrentUserUpdates = (setLoggedUser) => {
const friendRequestObserver$ = apolloClient.subscribe(
{ query: queries.NEW_FRIEND_REQUEST },
);
const followersUpdatesObserver$ = apolloClient.subscribe(
{ query: queries.FOLLOWERS_UPDATES },
);
const acceptedFriendRequestObserver$ = apolloClient.subscribe(
{ query: queries.ACCEPTED_FRIEND_REQUEST },
);
const friendRequestSubscription = friendRequestObserver$.subscribe({
next: ({ data: { newFriendRequest } }) => {
Alert.success(`${newFriendRequest.username} just sent you a friend request`);
setLoggedUser((loggedUser) => {
loggedUser.incomingFriendRequests.unshift(newFriendRequest._id);
});
},
error: err => console.error(err),
});
const followersUpdatesSubscription = followersUpdatesObserver$.subscribe({
next: ({ data: { followersUpdates: { follower, isFollow } } }) => {
if (isFollow) {
Alert.success(`${follower.username} is now following you`);
}
setLoggedUser((loggedUser) => {
isFollow
? loggedUser.followers.unshift(follower._id)
: loggedUser.followers.splice(loggedUser.followers.indexOf(follower._id), 1);
});
},
error: err => console.error(err),
});
const acceptedFriendRequestSubscription = acceptedFriendRequestObserver$.subscribe({
next: ({ data: { acceptedFriendRequest: newFriend } }) => {
Alert.success(`${newFriend.username} just accepted your friend request!`);
setLoggedUser((loggedUser) => {
loggedUser.friends.push(newFriend._id);
loggedUser.sentFriendRequests.splice(
loggedUser.sentFriendRequests.indexOf(newFriend._id), 1,
);
});
},
error: err => console.error(err),
});
return [
friendRequestSubscription,
followersUpdatesSubscription,
acceptedFriendRequestSubscription,
];
};
The way i subscribe from my component
const App = () => {
const currentUserSubscriptionRef = useRef();
useEffect(() => {
if (loggedUser && !currentUserSubscriptionRef.current) {
currentUserSubscriptionRef.current = subscribeToCurrentUserUpdates(
setLoggedUser,
);
}
if (!loggedUser && currentUserSubscriptionRef.current) {
currentUserSubscriptionRef.current.forEach((subscription) => {
subscription.unsubscribe();
});
currentUserSubscriptionRef.current = null;
}
}, [loggedUser, setLoggedUser]);
}