What is the shortest way to add redux to react-navigation? - reactjs

I'm integrating redux to my react-native application. Want to introduce Provider and to wrap screens in it. I'm using such constructions to register new screen:
Navigation.registerComponent('Login', () => LoginScreen);
As I see in documentation I need to wrap every component in <Provider store={store}>
And I'm just wondering if exist another way to do this, because if I have 10 screens(for example) in my app, I need to wrap them in provider ten times. I'll get code duplicates, but htis is not good

You can achieve this by creating an AppContainer component with all the components and wrap the AppContainer component inside the Provider.
import React, { Component } from "react";
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import { Provider as StoreProvider } from "react-redux";
const MainNavigator = createStackNavigator({
firstcomponentpath: FirstComponent,
secondcomponentpath: SecondComponent
)}
const AppContainer = createAppContainer(MainNavigator);
export default class App extends Component {
super(props);
this.state = {};
}
render() {
return (
<Provider store={store}>
<AppConatiner />
</Provider>
)}

You can try to directly add navigation to your store at the init :
HomePage.tsx
interface HomePageProps {
navigation: {
navigate: (pathName: string) => void;
};
}
const HomePage: FunctionComponent<HomePageProps> = (props) => {
const { navigation } = props;
initStore(navigation);
Store.ts
export const fetchInitStore = (props: HomePageProps) => {
const { navigation } = props;
const action: Actions = {
type: ActionType.INIT_STORE_STATE,
payload: {
Navigation: navigation,
},
};
return action;
};
export const initStore = (props: HomePageProps) => {
(store.dispatch as ThunkDispatch<State, unknown, Actions>)(fetchInitStore(props));
};
In you reducer :
case ActionType.INIT_STORE_STATE:
return actions.payload;
Then you can use it in other cases :
state.Navigation.navigation.navigate('Login');

Related

React Router 6 and TypeScript - rewrite WithRouter to be a typed wrapper [duplicate]

How can i access url parameter in my react component ?
App.js
<Route path="/question/:id" element={<QuestionView />} />
QuestionView.js
class QuestionView extends React.Component {
render() {
const { questions, users } = this.props;
const {id} = ???
Issue
In react-router-dom v6 the Route components no longer have route props (history, location, and match), and the current solution is to use the React hooks "versions" of these to use within the components being rendered. React hooks can't be used in class components though.
To access the match params with a class component you must either convert to a function component, or roll your own custom withRouter Higher Order Component to inject the "route props" like the withRouter HOC from react-router-dom v5.x did.
Solution
I won't cover converting a class component to function component. Here's an example custom withRouter HOC:
const withRouter = WrappedComponent => props => {
const params = useParams();
// etc... other react-router-dom v6 hooks
return (
<WrappedComponent
{...props}
params={params}
// etc...
/>
);
};
And decorate the component with the new HOC.
export default withRouter(Post);
This will inject a params prop for the class component.
this.props.params.id
HOC withRouter TypeScript version with generic Params
withRouter.tsx
import { ComponentType } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
export interface WithRouterProps<T = ReturnType<typeof useParams>> {
history: {
back: () => void;
goBack: () => void;
location: ReturnType<typeof useLocation>;
push: (url: string, state?: any) => void;
}
location: ReturnType<typeof useLocation>;
match: {
params: T;
};
navigate: ReturnType<typeof useNavigate>;
}
export const withRouter = <P extends object>(Component: ComponentType<P>) => {
return (props: Omit<P, keyof WithRouterProps>) => {
const location = useLocation();
const match = { params: useParams() };
const navigate = useNavigate();
const history = {
back: () => navigate(-1),
goBack: () => navigate(-1),
location,
push: (url: string, state?: any) => navigate(url, { state }),
replace: (url: string, state?: any) => navigate(url, {
replace: true,
state
})
};
return (
<Component
history={history}
location={location}
match={match}
navigate={navigate}
{...props as P}
/>
);
};
};
MyClass.tsx
import { Component } from 'react';
import { withRouter, WithRouterProps } from './withRouter';
interface Params {
id: string;
}
type Props = WithRouterProps<Params>;
class MyClass extends Component<Props> {
render() {
const { match } = this.props;
console.log(match.params.id); // with autocomplete
return <div>MyClass</div>;
}
}
export default withRouter(MyClass);
Here's the code example I'm using in my project to get the id from the URL:
import React from 'react'
import {Button} from 'antd'
import {useParams} from 'react-router-dom'
const DeleteUser = () => {
const {id} = useParams()
const handleDelete = async () => {
// handle delete function
}
return (
<Button onClick={handleDelete}>Delete User</Button>
)
}
export default DeleteUser
If you would like to use a class, then you will need to wrap it with the withRouter. I provide an example below:
This is my class for the movie form:
class MovieForm extends Form {
state = {
data: {
title: "",
genreId: "",
numberInStock: "",
dailyRentalRate: ""
},
genres: [],
errors: {}
};
schema = {
_id: Joi.string(),
title: Joi.string()
.required()
.label("Title"),
genreId: Joi.string()
.required()
.label("Genre"),
numberInStock: Joi.number()
.required()
.min(0)
.max(100)
.label("Number in Stock"),
dailyRentalRate: Joi.number()
.required()
.min(0)
.max(10)
.label("Daily Rental Rate")
};
componentDidMount() {
const genres = getGenres();
this.setState({ genres });
// const movieId = this.props.match.params.id;
const movieId = this.props.params.id;
if (movieId === "new") return;
const movie = getMovie(movieId);
if (!movie) return this.props.history.replace("/not-found");
this.setState({ data: this.mapToViewModel(movie) });
}
mapToViewModel(movie) {
return {
_id: movie._id,
title: movie.title,
genreId: movie.genre._id,
numberInStock: movie.numberInStock,
dailyRentalRate: movie.dailyRentalRate
};
}
doSubmit = () => {
saveMovie(this.state.data);
this.props.navigate("/movies");
};
render() {
return (
<div>
<h1>Movie Form</h1>
<form onSubmit={this.handleSubmit}>
{this.renderInput("title", "Title")}
{this.renderSelect("genreId", "Genre", this.state.genres)}
{this.renderInput("numberInStock", "Number in Stock", "number")}
{this.renderInput("dailyRentalRate", "Rate")}
{this.renderButton("Save")}
</form>
</div>
);
}
}
I write a wrapper outside of the class:
const withRouter = WrappedComponent => props => {
const params = useParams();
const navigate = useNavigate();
return (
<WrappedComponent
{...props}
params={params}
navigate={navigate}
/>
);
};
Now, at the end of the file I will export it like below:
export default withRouter(MovieForm);
Insdie the withRouter, I get all the functions that I will use later inside the class:
const params = useParams();
const navigate = useNavigate();
TypeScript version
withRouter.tsx
import React from 'react';
import {
useLocation,
useNavigate,
useParams,
NavigateFunction,
Params,
Location,
} from 'react-router-dom';
export interface RouterProps {
router: {
navigate: NavigateFunction;
readonly params: Params<string>;
location: Location;
}
}
function withRouter(Component: React.ComponentType<RouterProps>) {
const ComponentWithRouterProp: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
router={{ location, navigate, params }}
/>
);
};
return ComponentWithRouterProp;
}
export default withRouter;
MyComponent.tsx
import React from 'react';
import { connect } from 'react-redux';
import { RootState } from '##/redux/store';
import {
addSettings,
updateSettings,
} from '##/redux/mySlice';
import withRouter, { RouterProps } from '##/withRouter';
const mapState = (state: RootState) => ({
myStore: state.variation.myStore,
});
const mapDispatch = {
addSettings,
updateSettings,
};
type IProps = ReturnType<typeof mapState> & typeof mapDispatch & RouterProps;
class MyComponent extends React.Component<IProps> {
constructor(props: IProps) {
super(props);
}
onNavigateHome = () => {
this.props.router.navigate('/');
}
render(): React.ReactNode {
return (
<div className="test" onClick={this.onNavigateHome}>test</div>
);
}
}
export default withRouter(connect(mapState, mapDispatch)(MyComponent));
I had a similar issue, described here:
Params from React Router with class components and typescript
I created a new functional component, so I can use useParams():
import React from 'react';
import { useParams } from 'react-router';
type Props = {
children: JSX.Element
};
export const WithParams: React.FC<Props> = (props) => {
const params = useParams();
return React.cloneElement(props.children, {...props.children.props, ...params });
};
and added it to my Route.element
<Route path="/Contacts/VerifyEmailAddress/:id"
element={
<WithParams>
<VerifyEmail />
</WithParams>
}>
</Route>
and added the parameters I need to the props of my child component.
export class VerifyEmailProps {
public id?: string;
}

Get movieId from router v6 to component (movieDB) [duplicate]

How can i access url parameter in my react component ?
App.js
<Route path="/question/:id" element={<QuestionView />} />
QuestionView.js
class QuestionView extends React.Component {
render() {
const { questions, users } = this.props;
const {id} = ???
Issue
In react-router-dom v6 the Route components no longer have route props (history, location, and match), and the current solution is to use the React hooks "versions" of these to use within the components being rendered. React hooks can't be used in class components though.
To access the match params with a class component you must either convert to a function component, or roll your own custom withRouter Higher Order Component to inject the "route props" like the withRouter HOC from react-router-dom v5.x did.
Solution
I won't cover converting a class component to function component. Here's an example custom withRouter HOC:
const withRouter = WrappedComponent => props => {
const params = useParams();
// etc... other react-router-dom v6 hooks
return (
<WrappedComponent
{...props}
params={params}
// etc...
/>
);
};
And decorate the component with the new HOC.
export default withRouter(Post);
This will inject a params prop for the class component.
this.props.params.id
HOC withRouter TypeScript version with generic Params
withRouter.tsx
import { ComponentType } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
export interface WithRouterProps<T = ReturnType<typeof useParams>> {
history: {
back: () => void;
goBack: () => void;
location: ReturnType<typeof useLocation>;
push: (url: string, state?: any) => void;
}
location: ReturnType<typeof useLocation>;
match: {
params: T;
};
navigate: ReturnType<typeof useNavigate>;
}
export const withRouter = <P extends object>(Component: ComponentType<P>) => {
return (props: Omit<P, keyof WithRouterProps>) => {
const location = useLocation();
const match = { params: useParams() };
const navigate = useNavigate();
const history = {
back: () => navigate(-1),
goBack: () => navigate(-1),
location,
push: (url: string, state?: any) => navigate(url, { state }),
replace: (url: string, state?: any) => navigate(url, {
replace: true,
state
})
};
return (
<Component
history={history}
location={location}
match={match}
navigate={navigate}
{...props as P}
/>
);
};
};
MyClass.tsx
import { Component } from 'react';
import { withRouter, WithRouterProps } from './withRouter';
interface Params {
id: string;
}
type Props = WithRouterProps<Params>;
class MyClass extends Component<Props> {
render() {
const { match } = this.props;
console.log(match.params.id); // with autocomplete
return <div>MyClass</div>;
}
}
export default withRouter(MyClass);
Here's the code example I'm using in my project to get the id from the URL:
import React from 'react'
import {Button} from 'antd'
import {useParams} from 'react-router-dom'
const DeleteUser = () => {
const {id} = useParams()
const handleDelete = async () => {
// handle delete function
}
return (
<Button onClick={handleDelete}>Delete User</Button>
)
}
export default DeleteUser
If you would like to use a class, then you will need to wrap it with the withRouter. I provide an example below:
This is my class for the movie form:
class MovieForm extends Form {
state = {
data: {
title: "",
genreId: "",
numberInStock: "",
dailyRentalRate: ""
},
genres: [],
errors: {}
};
schema = {
_id: Joi.string(),
title: Joi.string()
.required()
.label("Title"),
genreId: Joi.string()
.required()
.label("Genre"),
numberInStock: Joi.number()
.required()
.min(0)
.max(100)
.label("Number in Stock"),
dailyRentalRate: Joi.number()
.required()
.min(0)
.max(10)
.label("Daily Rental Rate")
};
componentDidMount() {
const genres = getGenres();
this.setState({ genres });
// const movieId = this.props.match.params.id;
const movieId = this.props.params.id;
if (movieId === "new") return;
const movie = getMovie(movieId);
if (!movie) return this.props.history.replace("/not-found");
this.setState({ data: this.mapToViewModel(movie) });
}
mapToViewModel(movie) {
return {
_id: movie._id,
title: movie.title,
genreId: movie.genre._id,
numberInStock: movie.numberInStock,
dailyRentalRate: movie.dailyRentalRate
};
}
doSubmit = () => {
saveMovie(this.state.data);
this.props.navigate("/movies");
};
render() {
return (
<div>
<h1>Movie Form</h1>
<form onSubmit={this.handleSubmit}>
{this.renderInput("title", "Title")}
{this.renderSelect("genreId", "Genre", this.state.genres)}
{this.renderInput("numberInStock", "Number in Stock", "number")}
{this.renderInput("dailyRentalRate", "Rate")}
{this.renderButton("Save")}
</form>
</div>
);
}
}
I write a wrapper outside of the class:
const withRouter = WrappedComponent => props => {
const params = useParams();
const navigate = useNavigate();
return (
<WrappedComponent
{...props}
params={params}
navigate={navigate}
/>
);
};
Now, at the end of the file I will export it like below:
export default withRouter(MovieForm);
Insdie the withRouter, I get all the functions that I will use later inside the class:
const params = useParams();
const navigate = useNavigate();
TypeScript version
withRouter.tsx
import React from 'react';
import {
useLocation,
useNavigate,
useParams,
NavigateFunction,
Params,
Location,
} from 'react-router-dom';
export interface RouterProps {
router: {
navigate: NavigateFunction;
readonly params: Params<string>;
location: Location;
}
}
function withRouter(Component: React.ComponentType<RouterProps>) {
const ComponentWithRouterProp: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
router={{ location, navigate, params }}
/>
);
};
return ComponentWithRouterProp;
}
export default withRouter;
MyComponent.tsx
import React from 'react';
import { connect } from 'react-redux';
import { RootState } from '##/redux/store';
import {
addSettings,
updateSettings,
} from '##/redux/mySlice';
import withRouter, { RouterProps } from '##/withRouter';
const mapState = (state: RootState) => ({
myStore: state.variation.myStore,
});
const mapDispatch = {
addSettings,
updateSettings,
};
type IProps = ReturnType<typeof mapState> & typeof mapDispatch & RouterProps;
class MyComponent extends React.Component<IProps> {
constructor(props: IProps) {
super(props);
}
onNavigateHome = () => {
this.props.router.navigate('/');
}
render(): React.ReactNode {
return (
<div className="test" onClick={this.onNavigateHome}>test</div>
);
}
}
export default withRouter(connect(mapState, mapDispatch)(MyComponent));
I had a similar issue, described here:
Params from React Router with class components and typescript
I created a new functional component, so I can use useParams():
import React from 'react';
import { useParams } from 'react-router';
type Props = {
children: JSX.Element
};
export const WithParams: React.FC<Props> = (props) => {
const params = useParams();
return React.cloneElement(props.children, {...props.children.props, ...params });
};
and added it to my Route.element
<Route path="/Contacts/VerifyEmailAddress/:id"
element={
<WithParams>
<VerifyEmail />
</WithParams>
}>
</Route>
and added the parameters I need to the props of my child component.
export class VerifyEmailProps {
public id?: string;
}

How to set InitialState in React Testing Library

I am writing a test where I need to render a component, but the rendering of my component is not working and I am receiving this error:
Uncaught [TypeError: Cannot read property 'role' of undefined].
This is because in the componentDidMount function in my component I am checking if this.props.authentication.user.role === 'EXPERT'. However, this.props.authentication has user as undefined.
This is the correct initialState for my program, but for the test I want to set my initialState to have a user object. That is why I redefine initialState in my test. However, the component does not render with that new initialState.
Here is the testing file:
import { Component } from '../Component.js';
import React from 'react';
import { MemoryRouter, Router } from 'react-router-dom';
import { render, cleanup, waitFor } from '../../test-utils.js';
import '#testing-library/jest-dom/extend-expect';
afterEach(cleanup)
describe('Component Testing', () => {
test('Loading text appears', async () => {
const { getByTestId } = render(
<MemoryRouter><Component /></MemoryRouter>,
{
initialState: {
authentication: {
user: { role: "MEMBER", memberID:'1234' }
}
}
},
);
let label = getByTestId('loading-text')
expect(label).toBeTruthy()
})
});
Here is the Component file:
class Component extends React.Component {
constructor(props) {
super(props)
this.state = {
tasks: [],
loading: true,
}
this.loadTasks = this.loadTasks.bind(this)
}
componentDidMount() {
if (
this.props.authentication.user.role == 'EXPERT' ||
this.props.authentication.user.role == 'ADMIN'
) {
this.loadTasks(this.props.location.state.member)
} else {
this.loadTasks(this.props.authentication.user.memberID)
}
}
mapState(state) {
const { tasks } = state.tasks
return {
tasks: state.tasks,
authentication: state.authentication
}
}
}
I am also using a custom render function that is below
import React from 'react'
import { render as rtlRender } from '#testing-library/react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import { initialState as reducerInitialState, reducer } from './_reducers'
import rootReducer from './_reducers'
import configureStore from './ConfigureStore.js';
import { createMemoryHistory } from 'history'
function render(ui, {
initialState = reducerInitialState,
store = configureStore({}),
...renderOptions
} = {}
) {
function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions })
}
// re-export everything
export * from '#testing-library/react'
// override render method
export { render }
Perhaps I am coming super late to the party but maybe this can serve to someone. What I have done for a Typescript setup is the following (all this is within test-utils.tsx)
const AllProviders = ({
children,
initialState,
}: {
children: React.ReactNode
initialState?: RootState
}) => {
return (
<ThemeProvider>
<Provider store={generateStoreWithInitialState(initialState || {})}>
<FlagsProvider value={flags}>
<Router>
<Route
render={({ location }) => {
return (
<HeaderContextProvider>
{React.cloneElement(children as React.ReactElement, {
location,
})}
</HeaderContextProvider>
)
}}
/>
</Router>
</FlagsProvider>
</Provider>
</ThemeProvider>
)
}
interface CustomRenderProps extends RenderOptions {
initialState?: RootState
}
const customRender = (
ui: React.ReactElement,
customRenderProps: CustomRenderProps = {}
) => {
const { initialState, ...renderProps } = customRenderProps
return render(ui, {
wrapper: (props) => (
<AllProviders initialState={initialState}>{props.children}</AllProviders>
),
...renderProps,
})
}
export * from '#testing-library/react'
export { customRender as render }
Worth to mention that you can/should remove the providers that doesn't make any sense for your case (like probably the FlagsProvider or the HeaderContextProvider) but I leave to illustrate I decided to keep UI providers within the route and the others outside (but this is me making not much sense anyway)
In terms of the store file I did this:
//...omitting extra stuff
const storeConfig = {
// All your store setup some TS infer types may be a extra challenge to solve
}
export const store = configureStore(storeConfig)
export const generateStoreWithInitialState = (initialState: Partial<RootState>) =>
configureStore({ ...storeConfig, preloadedState: initialState })
//...omitting extra stuff
Cheers! 🍻
I am not sure what you are doing in the configure store, but I suppose the initial state of your component should be passed in the store.
import React from 'react'
import { render as rtlRender } from '#testing-library/react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import { initialState as reducerInitialState, reducer } from './_reducers'
import { createMemoryHistory } from 'history'
function render(
ui,
{
initialState = reducerInitialState,
store = createStore(reducer,initialState),
...renderOptions
} = {}
) {
function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions })
}
// re-export everything
export * from '#testing-library/react'
// override render method
export { render }
I hope it will help you :)

React hooks: useState/context; Cannot read property 'avatar' of undefined/How to update a nested object

I have passed down a state variable and function from a context file:
UserContext:
import React, { useState } from 'react';
const UserContext = React.createContext();
function UserProvider({ children }) {
var [userImages, setUserImages] = useState({
avatar: '/static/uploads/profile-avatars/placeholder.jpg'
});
return (
<UserContext.Provider
value={{
userImages,
setUserImages
}}
>
{children}
</UserContext.Provider>
);
}
export default UserContext;
export { UserProvider };
At this point UserImages is just an object with one prop i.e. avatar
This is my App which is being wrapped by the Provider (please disregard the redux implementation, I just wanted to try Context)
import React from 'react';
import { Provider } from 'react-redux';
import { UserProvider } from './UserContext';
import App from 'next/app';
import withRedux from 'next-redux-wrapper';
import { PersistGate } from 'redux-persist/integration/react';
import reduxStore from '../store/index';
import withReactRouter from '../with-react-router/with-react-router';
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};
return { pageProps };
}
render() {
const { Component, pageProps, store } = this.props;
return (
<UserProvider>
<Provider store={store}>
<PersistGate persistor={store.__PERSISTOR} loading={null}>
<Component {...pageProps} />
</PersistGate>
</Provider>
</UserProvider>
);
}
}
I am trying to update some context using a setState function following this post
However I still get TypeError: Cannot read property 'avatar' of undefined
This is the shape of the state object:
userData:
setUserImages: ƒ ()
userImages:
avatar: "/static/uploads/profile-avatars/placeholder.jpg"
or
userData : {
setUserImages : SetUserImages function,
userImages : {
avatar : "/static/uploads/profile-avatars/placeholder.jpg"
}
}
My component:
function ImageUploader({ userData }) {
var { avatar } = userData.userImages;
var setUserAvatar = userData.setUserImages;
function avatarUpdater(avatarPath) {
setUserAvatar({ userData: { ...userData.userImages.avatar, avatarPath } });
}
}
Does anyone have an idea why this is happening?
UserProvider is the root of your app, so you can directly get it {userImages, setUserImages} in ImageUploader
function ImageUploader() {
const {userImages, setUserImages} = useContext(UserContext)
const { avatar } = userImages;
function avatarUpdater(avatarPath) {
setUserImages({ avatar: avatarPath });
}
}
Typically its good practice to not expose the setState from your context. You wanna wrap it in an explicit method to update state, then add that method to your Provider. Something like:
const userContext = {
avatar: userImages,
updateAvatarUrl: (url) => {
setUserImages(prevState => ({...prevState, avatar: url}))
}
}
return <UserContext.Provider value={userContext}>{children}</UserContext.Provider>
Try adding a hook for your UserContext which you can consume inside your component.
In UserContext add
export const useUserContext = () => useContext(UserContext)
Then inside your component:
import { useUserContext } from '<UserContext import>'
...
function avatarUpdater(avatarPath) {
userCtx.updateAvatarUrl(avatarPath)
}
Cleanest structure for Context in my opinion. Allows for more precise control over context state.

Having issues with dynamic routes on Next 9

GraphQL queries in my components are not running on dynamic routes when I try to access the query string with router.query.xxx.
I have the following file
// ./pages/section/[slug].js
import { useRouter } from 'next/router';
import AppLayout from '../../components/styles/_AppLayout';
const Section = () => {
const router = useRouter();
return <AppLayout>Hi</AppLayout>;
};
export default Section;
The page displays fine, but as soon as I add {router.query.slug} and refresh the page, it gives me a TypeError because the GraphQL queries do not run. As you can see in the image below, me.firstName is undefined because the GraphQL query did not run
This is the code in _AppLayout.js
import styled from 'styled-components';
import Navigation from '../Navigation';
const Wrapper = styled.div`...`;
const AppLayout = props => {
return (
<Wrapper>
<Navigation />
<main>{props.children}</main>
</Wrapper>
);
};
export default AppLayout;
Any ideas why this might be happening and how to fix it?
Thanks
I was able to solve my issue two ways
Using withRouter
import { withRouter } from 'next/router';
import TestComponent from '../../components/TestComponent';
import AppLayout from '../../components/styles/_AppLayout';
const Section = props => {
return <AppLayout>Hi {props.query.slug}</AppLayout>;
};
export default withRouter(Section);
and passing the query parameter as props via getInitialProps
const Section = ({slug}) => {
return <AppLayout>Hi {slug}</AppLayout>;
};
Section.getInitialProps = async ({ query }) => {
const { slug } = query;
return { slug };
};
export default Section;
The following method worked for me, I am using React Hooks with Context and I need to also use the nextJS route with it, so following configuration can be followed.
Note: If you are using GraphQL then that can be also wrapped around the final JSX in _app.js
_app.js:
import { withRouter } from "next/router";
BuilderProvider is here Context Provider
const InjectRouterContext = withRouter(({ router, children }) => {
return <BuilderProvider value={router}>{children}</BuilderProvider>;
});
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<InjectRouterContext>
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
</InjectRouterContext>
);
}
}
Now in the Page, here it is somepage.js:
import { useRouter } from "next/router";
const somepage = () => {
const router = useRouter();
const { id } = router.query;
return (//JSX Here);
}
somepage.getInitialProps = async ({ query }) => {
const { slug } = query;
return { slug };
};
export default somepage;

Resources