Can you use useState (and other react hooks?) with Server Side Rendering? Everytime I am trying to run the code below I get the error TypeError: Cannot read property 'useState' of null. However, when I comment out the getServerSideProps function at the very bottom I have no problem running the code as intended. So my questions is can useState be used with Server Side Rendering in nextjs? If the answer is yes, then where am I going wrong in the code below?
import React from "react";
import { useRouter } from "next/router";
import useSelectedGenreInfoExtractor from "../../hooks/useSelectedGenreInfoExtractor";
import { useState } from "react";
import { useEffect } from "react";
import Navbar from "../../components/Navbar";
import useFetchTrendingCatagory from "../../hooks/useFetchTrendingCatagory";
import useFetchTopRatedCatagory from "../../hooks/useFetchTopRatedCatagory";
import useFetchMovieGenreResults from "../../hooks/useFetchMovieGenreResults";
import Moviegenreresults from "../../components/Moviegenreresults";
export default function genre(props) {
const [myresultsfromhook, setMyresultsfromhook] = useState();
const [myreturnedmovies, setMyreturnedmovies] = useState();
const router = useRouter();
const { genre } = router.query;
if (genre == "Trending") {
let mymovies = useFetchTrendingCatagory();
console.log("This is a log of my props", props);
return (
<div>
{/* <Navbar /> */}
<div>{genre}</div>
<Moviegenreresults movies={mymovies} />
</div>
);
} else if (genre == "Top Rated") {
let mymovies = useFetchTopRatedCatagory();
return (
<div>
{/* <Navbar /> */}
<div>{genre}</div>
<Moviegenreresults movies={mymovies} />
</div>
);
} else {
let mymovies = useFetchMovieGenreResults(genre);
return (
<div>
{/* <Navbar /> */}
<div>{genre}</div>
<Moviegenreresults movies={mymovies} />
</div>
);
}
}
export async function getServerSideProps(context) {
if (context.params.genre == "Trending") {
let mymovies = useFetchTrendingCatagory();
return {
props: {
results: mymovies.results,
},
};
} else if (context.params.genr == "Top Rated") {
let mymovies = useFetchTopRatedCatagory();
return {
props: {
results: mymovies.results,
},
};
} else {
let mymovies = useFetchMovieGenreResults(genre);
return {
props: {
results: mymovies.results,
},
};
}
}
I think fundamentally the problem is the way you are using getServerSideProps.
Even thought the answer is you can not use useState inside getServerSideProps because this function run in the server, it is important to understand what getServerSideProps does and when, I think you can find very clear explanation about that in next docs.
https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
Inside getServerSideProps use axios or the fetch api to get your data and pass it to the props.
I am not 100% sure but I thinnk inn your case you can also use Promise.all() to get the data from those three api calls.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
useState should be inside the component, it is a React hook. serverside functions are independent of React components.
I think the issue is the name of the component should be with capital letter:
// not genre
export default function Genre(props)
Related
I am having a ton of react hooks a missing dependency warnings for my redux functions that are called in useEffect to fetch data. The official documentation and dan abramov's post about this issue doesn't help me so much on what to do. Since, in summary, the recommendations are ;
move the function inside the useEffect if it is used in an effect only (I would rather not because I am using these functions elsewhere as well.)
move the function outside the component if it is not using any props or state ( they are already declared in their own store and I bring them to the component's context via connecting tag. And so they appear on the props object of the component. So this solution is also not applicable.
wrap them with useCallback so you can add them in the dependency array and it won't lead to an infinite loop because the functions will not get a new reference on each render. ( this sounds a little bit hacky solution to me. But if not please enlighten me. So it will look like this;
const { fetchMe } = props
useEffect(() => {
fetchMe()
}, [])
will turn into this
const { fetchMe } = props
const initFetchMe = useCallback(() => {
return dispatch(fetchMe())
}, [dispatch])
useEffect(() => {
initFetchMe();
}, [initFetchMe]);
When I look at the stackoverflow some candidate solution for this to refactor connect tag with redux hooks (useDispatch and useSelector) I am more leaning towards this solution but I couldn't find a good migration guide, I don't want to be introducing lots of bugs at once to the repo. So this example component:
import React, {useEffect} from 'react'
import Stats from '../components/Stats'
import {useTranslation} from 'react-i18next'
import {connect} from 'react-redux'
import {getEmployeesTotal} from '../stores/employees'
import {getVisitorsTotal} from '../stores/visitors'
import {getCardsTotal} from '../stores/cards'
function SimpleStats(props) {
const {t} = useTranslation()
useEffect(() => {
props.getEmployeesTotal()
props.getCardsTotal()
props.getVisitorsTotal()
}, [])
return (
<React.Fragment>
<div className="container">
<div className="row quick-stats justify-content-md-center">
<Stats
className="col"
value={
props.get_employees_total !== null ? props.get_employees_total.meta.total : '...'
}
description={t('stats.EmployeesTotal')}
/>
<Stats
className="col"
value={
props.get_visitors_total !== null ? props.get_visitors_total.meta.total : '...'
}
description={t('stats.VisitorsTotal')}
/>
<Stats
className="col"
value={props.get_cards_total !== null ? props.get_cards_total.meta.total : '...'}
description={t('stats.CardsTotal')}
/>
</div>
</div>
</React.Fragment>
)
}
const mapStateToProps = (state) => ({
get_employees_total: state.employees.get_employees_total.success,
get_visitors_total: state.visitors.get_visitors_total.success,
get_cards_total: state.cards.get_cards_total.success,
})
export default connect(mapStateToProps, {
getEmployeesTotal,
getVisitorsTotal,
getCardsTotal,
})(SimpleStats)
becomes this:
import React, {useEffect} from 'react'
import Stats from '../components/Stats'
import {useTranslation} from 'react-i18next'
import {connect} from 'react-redux'
import {getEmployeesTotal} from '../stores/employees'
import {getVisitorsTotal} from '../stores/visitors'
import {getCardsTotal} from '../stores/cards'
function SimpleStats(props) {
const {t} = useTranslation()
const get_employees_total = useSelector((state)=>state.employees.get_employees_total.success)
const get_visitors_total = useSelector((state) => state.visitors.get_visitors_total.success )
const get_cards_total = useSelector((state) => state.cards.get_cards_total.success)
useEffect(() => {
dispatch(getEmployeesTotal())
dispatch(getCardsTotal())
dispatch(getVisitorsTotal())
}, [dispatch])
return (
<React.Fragment>
<div className="container">
<div className="row quick-stats justify-content-md-center">
<Stats
className="col"
value={
props.get_employees_total !== null ? props.get_employees_total.meta.total : '...'
}
description={t('stats.EmployeesTotal')}
/>
<Stats
className="col"
value={
props.get_visitors_total !== null ? props.get_visitors_total.meta.total : '...'
}
description={t('stats.VisitorsTotal')}
/>
<Stats
className="col"
value={props.get_cards_total !== null ? props.get_cards_total.meta.total : '...'}
description={t('stats.CardsTotal')}
/>
</div>
</div>
</React.Fragment>
)
}
export default SimpleStats
Would you enlighten me on which solution is better? Does my migration to redux-hooks have problems?
I want to pass the setState method of the component (SnackBar) to all the child components of the _app.js. If I pass the setState method of SnackBar to all the child components of _app.js then it will be a very tedious task. Because, there are approx 4 levels of hierarchy from _app.js to the single component node. It includes,
_app.js -> pages -> layouts -> sections -> components
The snippet of _app.js is here.
function MyApp({ Component, pageProps }) {
const [ toastOpen, setToastOpen ] = React.useState({
msg: '',
open: false
});
React.useEffect(() => {
pageProps = { ...pageProps, setToastOpen };
}, []);
return (
<React.Fragment>
<ToastMessage
message={ toastOpen.msg }
setOpenState={ setToastOpen }
openState={ toastOpen.open }
/>
<Component {...pageProps} />
</React.Fragment>
)
}
Is there any way that I can directly import the setToastOpen method in the child component and use it whenever I need it?
React have a special Feature called Context Api , using that you can skip the props chain passed into your components..
I recomend you to checkout below resources to learn about context Api -
https://reactjs.org/docs/context.html
https://www.freecodecamp.org/news/react-context-in-5-minutes
Example of ContextAPI
Create a seperate file for Context Toast-context.js , You can use any name you want.
import React, { useState } from "react"
const ToastContext = React.createContext({
message: "",
toastOpen: false,
toggleToast: () => { },
changeMessage: () => { }
})
export const ToastContextProvider = ({children}) => {
/*you need to use
this component to wrap App.js so that the App.js and all of its children
and their children components and so on will get the access to the
context*/
const [toastOpen, setToastOpen] = useState(false);
const [message, setMessage] = useState("");
const toggleToast = () => {
setToastOpen(true)
}
const changeMessage = (message) => {
setMessage(message);
}
return (
<ToastContext.Provider value={
toastOpen,
message,
toggleToast,
changeMessage
}>
{children}
</ToastContext.Provider>
)
}
now in the App.js file you need to wrap your components with ToastContextProvider component
import React, { useContext } from "react";
import { ToastContextProvider } from "./Toast-context";
import { ToastContext } from "./Toast-context";
function MyApp({ Component, pageProps }) {
const { message, toastOpen, toggleToast, changeMessage } =
useContext(ToastContext);
return (
<ToastContextProvider>
{toastOpen && <div className="toast">{message}</div>}
</ToastContextProvider>
);
}
just import the context using useContext Hook in any component you want. you don't need to wrap with <ToastContextProvider> in every component.
just use useContext hook and then you can see the state and as well as call the functions methods to change the state.
Also make sure to refer the above links to learn more about Context Api. Thank You
I have a simple react app in which i have to use useContext.
(btw im using vite + react)
here is my code for Context.jsx
import React, {useContext} from 'react';
const emailContext = React.createContext();
export const useEmail = () => useContext(emailContext);
export const emailProvider = ({children}) => {
const currentUser = "None";
const value = {
currentUser
}
return(
<emailContext.Provider value={value}>
{children}
</emailContext.Provider>
)
}
and heres how i am using the context
import "./styles.css";
import { useEmail } from "./Context/Context"
export default function App() {
const {currentUser} = useEmail();
return (
<div className="App">
<h1>Hello CodeSandbox {currentUser}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
I am sure why I am getting error in this code.
some of the errors that I am getting
_useEmail is undefined (latest)
currentUser user is undefined
thing i have tried
Initialized createContext with some initial value (only intial value is visible).
using useContext() directy in the App.js (useContext(emailContext) return undefined)
instead of {children} used <children/>.
used useState instead of const currentUser in emailProvider
I am getting same problem even when I use typescript.
but none of the above helped.
You should wrapping app with <emailProvider></emailProvider> to using data in value={value}. Now it gets undefined from const emailContext = React.createContext();
Below code may help you analyse the flow , also check link for more details https://medium.com/technofunnel/usecontext-in-react-hooks-aa9a60b8a461
use useContext in receiving end
import React, { useState } from "react";
var userDetailContext = React.createContext(null);
export default function UserDetailsComponent() {
var [userDetails] = useState({
name: "Mayank",
age: 30
});
return (
<userDetailContext.Provider value={userDetails}>
<h1>This is the Parent Component</h1>
<hr />
<ChildComponent userDetails={userDetails} />
</userDetailContext.Provider>
);
}
function ChildComponent(props) {
return (
<div>
<h2>This is Child Component</h2>
<hr />
<SubChildComponent />
</div>
);
}
function SubChildComponent(props) {
var contextData = React.useContext(userDetailContext);
return (
<div>
<h3>This is Sub Child Component</h3>
<h4>User Name: {contextData.name}</h4>
<h4>User Age: {contextData.age}</h4>
</div>
);
}
So im trying to get req query aka GET params from the url into server side rendering for basicly validation and / login for authendication for example shopify shop but i cant really verify it or parse the shop before its parsed to the renderes page aka a component
I have tried both geninitialprops and getserverside props but its not returning the values in the url like /app?mydata=thisisworking&hmac=21SA92
My code
// _app.js
export default function InvenGroupApp({ Component, pageProps }) {
const { pathname } = useRouter();
const isApp = pathname.startsWith('/app');
return (
<React.Fragment>
<Head>
<title>InvenGroup</title>
<meta charSet="utf-8" />
</Head>
<PolarisProvider theme={theme}>
{isApp
? <AppHandler Component={Component} {...pageProps} />
: <Component {...pageProps} />
}
</PolarisProvider>
</React.Fragment>
);
}
The direct component that should handle it
// #components/AppHandler.js
import React, { Component } from 'react';
import AppLayout from "#components/AppLayout";
import shopSession from '#lib/shop';
export async function getServerSideProps(context) {
return {
props: { context }
}
}
export default class AppHandler extends Component {
render () {
const { shop } = shopSession();
const AppPage = this.props.Component;
console.log(this.props);
if (shop) {
return (
<AppLayout Component={AppPage}/>
)
}
return (
<AppPage>
<div>Loading</div>
</AppPage>
)
}
}
To get the query parameters, you probably don't want to pass the full context object as props - way too much unclear and unnecessary data.
export async function getServerSideProps(context) {
// The query params are set on `context.query`
const { query } = context
return {
props: { query }
}
}
In your example url of /app?mydata=thisisworking&hmac=21SA92, this would give your class component this.props.mydata and this.props.hmac.
I'm trying to port from class component to react hooks with Context API, and I can't figure out what is the specific reason of getting the error.
First, my Codes:
// contexts/sample.jsx
import React, { createContext, useState, useContext } from 'react'
const SampleCtx = createContext()
const SampleProvider = (props) => {
const [ value, setValue ] = useState('Default Value')
const sampleContext = { value, setValue }
return (
<SampleCtx.Provider value={sampleContext}>
{props.children}
</SampleCtx.Provider>
)
}
const useSample = (WrappedComponent) => {
const sampleCtx = useContext(SampleCtx)
return (
<SampleProvider>
<WrappedComponent
value={sampleCtx.value}
setValue={sampleCtx.setValue} />
</SampleProvider>
)
}
export {
useSample
}
// Sends.jsx
import React, { Component, useState, useEffect } from 'react'
import { useSample } from '../contexts/sample.jsx'
const Sends = (props) => {
const [input, setInput ] = useState('')
const handleChange = (e) => {
setInput(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault()
props.setValue(input)
}
useEffect(() => {
setInput(props.value)
}, props.value)
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
)
}
Error I got:
Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
Explanation for my code:
I used Context API to manage the states, and previously I used class components to make the views. I hope the structure is straightforward that it doesn't need any more details.
I thought it should work as well, the <Sends /> component gets passed into useSample HoC function, and it gets wrapped with <SampleProvider> component of sample.jsx, so that <Sends /> can use the props provided by the SampleCtx context. But the result is failure.
Is it not valid to use the HoC pattern with React hooks? Or is it invalid to hand the mutation function(i.e. setValue made by useState()) to other components through props? Or, is it not valid to put 2 or more function components using hooks in a single file? Please correct me what is the specific reason.
So HOCs and Context are different React concepts. Thus, let's break this into two.
Provider
Main responsibility of the provider is to provide the context values. The context values are consumed via useContext()
const SampleCtx = createContext({});
export const SampleProvider = props => {
const [value, setValue] = useState("Default Value");
const sampleContext = { value, setValue };
useEffect(() => console.log("Context Value: ", value)); // only log when value changes
return (
<SampleCtx.Provider value={sampleContext}>
{props.children}
</SampleCtx.Provider>
);
};
HOC
The consumer. Uses useContext() hook and adds additional props. Returns a new component.
const withSample = WrappedComponent => props => { // curry
const sampleCtx = useContext(SampleCtx);
return (
<WrappedComponent
{...props}
value={sampleCtx.value}
setValue={sampleCtx.setValue}
/>
);
};
Then using the HOC:
export default withSample(Send)
Composing the provider and the consumers (HOC), we have:
import { SampleProvider } from "./provider";
import SampleHOCWithHooks from "./send";
import "./styles.css";
function App() {
return (
<div className="App">
<SampleProvider>
<SampleHOCWithHooks />
</SampleProvider>
</div>
);
}
See Code Sandbox for full code.
Higher order Components are functions that takes a Component and returns another Component, and the returning Components can be class component, a Functional Component with hooks or it can have no statefull logic.
In your example you're returning jsx from useSample.
const useSample = (WrappedComponent) => {
const sampleCtx = useContext(SampleCtx)
return ( // <-- here
<SampleProvider>
<WrappedComponent
value={sampleCtx.value}
setValue={sampleCtx.setValue} />
</SampleProvider>
)
}
if you want to make a HOC what you can do is something like this
const withSample = (WrappedComponent) => {
return props => {
const sampleCtx = useContext(SampleCtx)
<WrappedComponent
value={sampleCtx.value}
setValue={sampleCtx.setValue} {...props} />
}
}