Import global vs getStaticProps in Next/React Component - reactjs

So I'm new in Next.js and I'm wondering, in my index.js file in pages I have a component that will return a list from a JSON object. So whats the difference between importing this JSON file outside my component or getting it inside getStaticProps?
import contactList from '../../public/contactList.json'
export default function Home () {
// ... component here
}
OR
export async function getStaticProps() {
import contactList from '../../public/contactList.json'
return {
props: {contactList}
}
}
export default function Home () {
// ... component here
}
OR even i could instead set an environment variable in Vercel for using fetch.
Whats the pros and cons of each of these?
EDIT: I should use import(adress) inside getStaticProps, witch will return an object with the json "array" in a default param, so correcting myself:
export async function getStaticProps() {
const list = await import ('../../public/contactList.json');
return {
props: { contactList: list.default }
}
}

Basically it boils down to better performance. From the Next.js docs:
Note: You can import modules in top-level scope for use in getStaticProps. Imports used in getStaticProps will not be bundled for the client-side.
So the data will be fetched at build time if you use the import in getStaticProps and the user accessing the website will have to download fewer data which will result in a faster page.
EDIT:
The Next.js docs explicitly state not to use fetch to get internal data:
Note: You should not use fetch() to call an API route in getStaticProps. Instead, directly import the logic used inside your API route. You may need to slightly refactor your code for this approach.
Fetching from an external API is fine!

Related

Using React.Context with Nextjs13 server-side components

Next13 was released a week ago, and I am trying to migrate a next12 app to a next13.
I want to use server-side components as much as possible, but I can't seem to use
import { createContext } from 'react';
in any server component.
I am getting this error:
Server Error
Error:
You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
,----
1 | import { createContext } from 'react';
: ^^^^^^^^^^^^^
`----
Maybe one of these should be marked as a client entry with "use client":
Is there an alternative here or do I have to resort to prop drilling to get server-side rendering?
It seems like I can use createServerContext
import { createServerContext } from 'react';
If you're using Typescript and React 18, you'll also need to add "types": ["react/next"] to your tsconfig.json compiler options, since this is a not-yet-stable function.
This is a new feature from React's SSR to recognize whether a component is client-side or server-side. In your case, createContext is only available on the client side.
If you only use this component for client-side, you can define 'use client'; on top of the component.
'use client';
import { createContext } from 'react';
You can check this Next.js document and this React RFC for the details
According to Next.js 13 beta documentation, you cannot use context in Server Components:
In Next.js 13, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated
However, there are alternative ways to handle data in the new approach, depending on your case. F.e. if you fetched the data from the server in a parent component and then passed it down the tree through Context, you can now fetch the data directly in all the components that depend on this data. React 18 will dedupe (de-duplicate) the fetches, so there are no unnecessary requests.
There are more alternatives in the documentation.
I've made a tiny package to handle context in server components, works with latest next.js, it's called server-only-context:
https://www.npmjs.com/package/server-only-context
Usage:
import serverContext from 'server-only-context';
export const [getLocale, setLocale] = serverContext('en')
export const [getUserId, setUserId] = serverContext('')
import { setLocale, setUserId } from '#/context'
export default function UserPage({ params: { locale, userId } }) {
setLocale(locale)
setUserId(userId)
return <MyComponent/>
}
import { getLocale, getUserId } from '#/context'
export default function MyComponent() {
const locale = getLocale()
const userId = getUserId()
return (
<div>
Hello {userId}! Locale is {locale}.
</div>
)
}
This is the code for it, it's really simple:
import 'server-only'
import { cache } from 'react'
export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
const getRef = cache(() => ({ current: defaultValue }))
const getValue = (): T => getRef().current
const setValue = (value: T) => {
getRef().current = value
}
return [getValue, setValue]
}

react-query useQuery inside a provider with with dynamic parameters

I'm using react query because it's super powerful but I'm struggling trying to share my data across many components inside a provider. I'm wondering if this is the right approach.
PostsContext.js
import React, {useState} from 'react';
import { useTemplate } from '../hooks';
export const PostsContext = React.createContext({});
export const PostsProvider = ({ children }) => {
const fetchTemplate = useTemplate(templateId);
const context = {
fetchTemplate,
};
return <PostsContext.Provider value={context}>{children}</PostsContext.Provider>;
};
useTemplate.js
import React from 'react';
import { useQuery } from 'react-query'
import { getTemplateApi } from "../api";
export default function useTemplate(templateId) {
return useQuery(["templateId", templateId], () => getTemplateApi(templateId), {
initialData: [],
enabled:false,
});
}
and then my component that uses the context
function Posts () {
const { fetchTemplate } = useContext(PostsContext);
console.log(fetchTemplate.isLoading)
fetchTemplate.refetch() <---- how can I refetch with a different templateId?
return {...}
}
I'm looking for a way to dynamically call my hook with a different templateId but with the hook inside the provider so I can use it all over my app. Is this the right approach? I have deeply nested components that I don't want to prop drill.
You don’t need an extra way to distribute your data, like react context. Just call useQuery with the same key wherever you need to, and react query will do the rest. It is best to abstract that away in a custom hook.
refetch should only be used if you want to refetch with the exact same parameters. For changing parameters, it’s best to. make them part of your query key, because react query will refetch whenever the query key changes.
So in your example, you only need to call useTemplate with a different templateId. templateId itself is local state (which template has been selected by the user or so), and how you make that globally available is up to you.

How to use GraphQL mutation in Next.js without useMutation

I'm working on a codebase with Next.js version 9.3.0 and GraphQL. To get the benefits of Next.js optimizations, we are wrapping each page in withApollo, so inside the pages we can make use of useQuery, useMutation.
The issue I'm facing is that I need to use mutation in the Header component which is outside the page, which doesn't have access to ApolloClient because the app is not wrapped in withApollo.
The error I'm getting is this Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance in via options.
The Header component is inside the Layout component like this:
<>
<Meta />
<Header/>
{children} // pages which are composed with withApollo
<Footer />
</>
Instead of using useQuery in a component without withApollo is easy you can use
import { createApolloFetch } from 'apollo-fetch';
const fetch = createApolloFetch({
uri: process.env.GRAPHQL_SERVER,
});
const userInfoQuery = `{
userInfo {
loggedIn
basketCount
wishListCount
}
}`;
const userInfoData = fetch({
query: userInfoQuery,
});
Is there an alternative solution for useMutation in a component not composed with withApollo?
Any suggestions are welcomed,
Cheers
Silly me, mutations work with Apollo fetch too
https://github.com/apollographql/apollo-fetch#simple-graphql-mutation-with-variables but this is not maintained anymore
The solution that worked for me, in the end, was to still useMutation and to pass the client to it.
https://www.apollographql.com/docs/react/data/mutations/#usemutation-api
import { useMutation } from '#apollo/react-hooks';
import createApolloClient from 'lib/apolloClient';
import loginUser from 'mutations/login';
const [getToken, { data: mutationData, error: loginErrors, loading }] = useMutation(loginUser, { client: createApolloClient()});

How to import and call a react pure function which is using redux connect and its functions? (Only React functional components no classes)

Suppose I have a react pure function named SignIn() in One.js :
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {GoogleSignin, statusCodes} from '#react-native-community/google-signin';
import {getToken, saveToken} from '../actions/token';
const SignIn = async ({token, getToken, saveToken}) => {
const savedToken = await getToken();
console.log(token.loading, savedToken);
SignIn.propTypes = {
token: PropTypes.string.isRequired,
getToken: PropTypes.func.isRequired,
saveToken: PropTypes.func.isRequired,
};
};
const mapStateToProps = state => {
console.log('state : ', state);
return {
token: state.token,
};
};
export default connect(mapStateToProps, {saveToken, getToken})(SignIn);
I want to use this SignIn() function in another Two.js react file so that getToken() which is a redux function and other functions will be called inside file One.js and then i can use those functions inside file Two.js but the problem is because of redux connect, i am not able to export and use them. How can i import and use this kind of function inside Two.js file ?
connect function can only be implemented with react components that renders actual jsx, and for it to work you need to return jsx elements or null and call it like this <SignIn />.. in my opinion if you want to implement some logic with the use of redux, you can make a custom hook, implement useSelector or useDispatch inside it, and either return the data you want or just do your effect inside it then return nothing.
hope this helps.
here's an example from react-redux docs https://react-redux.js.org/api/hooks#usedispatch
What Worked for me was declaring the functions that i want to export inside the redux actions, so i created a new action for any function that i want to use. Make sure to make use of loading state of initial state otherwise functions can be called infinite times because of re-rendering.

Nextjs redux, thunk and getInitialProps - how to implement

I want to use nextjs in my new project with redux and thunk also. I wondering how to implement all packages correctly.
In my previous projects pages has HOC components like:
import {connect} from 'react-redux';
import Page from './about';
import {fetchUsers} from '../../actions/user';
const mapStateToProps = (state) => {
const {users} = state;
return users;
};
const mapDispatchToProps = (dispatch) => {
return {
fetchUsers: () => dispatch(fetchUsers())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Page);
And method to fetch users I implemented in componentDidMount
How to implement the same logic for nexjs?
What have I do?
Implemented store (base on next-redux-wrapper in _app.js)
Created HOC component (like below) with mapStateToProps and
mapDispatchToProps
Currently I thinking about use somehow this.props.fetchUsers method into getInitialProps - documentation say that this method should be used to fetch data before render site.
Please help me with correctly redux implementation for nextjs
You can follow this example
The correct way is to pass the store to the getInitialProps context and to the App component so you can pass it to the Provider.
The getInitialProps can't access to instance of the component, this is not accessible, so you can't call this.props.fetchUsers, but, because you are passing store to its context, you can do store.dispatch(fetchUsers()) and remove dispatch from mapDispatchToProps.
Generally I dispatch actions in getInitialProps and then map state to props within connect.

Resources