8

Im building an app with NextJS. My app displays a list of post and the user has the ability to sort the list from A-Z or Z-A and to display a specific amount of posts per page (10,20,etc). When the user clicks on a post to visit that specific post page and then go back to the main list, the sorting and pagination preferences were getting reset, I managed to keep the preserved the values using cookies, but I would like to use useContext() instead. For this app I have a Layout.js file, and thought that would be the right place to insert my Provider like this:

import React, {useState} from 'react';
import Navbar from './Navbar';
import Head from 'next/head';
import {SortingContext} from './UseContext';

const Layout = (props)=> {
  const [value, setValue] = useState('hello');

  return (<SortingContext.Provider value={{value, setValue}}>
            <div>
                <Head>
                  <title>MyApp</title>
                </Head>
                <Navbar/>
                {props.children}
            </div>
            </SortingContext.Provider>
        )};

But when I try to get the value from one of my pages, I get TypeError: Cannot read property 'value' of null

I'm using useContext somewhere else in my app, so I know I can get it to work. I just don't understand where to put it in my NextJS app, so the value will persist even if I visit a different page.

This is my index.js where I'm trying to print the value:

import React, { useState, useEffect, useContext } from 'react';
import withData from '../lib/apollo';
import Layout from '../components/Layout';
import {SortingContext} from '../components/UseContext';
import Footer from '../components/Footer';

const Home = () => {

  const {value, setValue} = useContext(SortingContext);

  return (
    <Layout>
      <div className='main_screen'>
  <h1>{value}</h1>
      </div>
      {siteOptions && <Footer />}
    </Layout>
  )
};

export default withData(Home);

And my UseContext.js:

import {createContext} from 'react';

export const SortingContext = createContext(null);

Carlos G
  • 479
  • 1
  • 8
  • 19

2 Answers2

5

The issue is you're attempting to useContext higher up in the tree from where the context is being provided from. Right now, your provider is in the Layout, however, you're trying to use it in Home, which is the parent of the layout. So a couple of things you can do, you can move your provider higher up outside of Home, or if you want to keep your current structure, you can do the following:

const Home = () => {

  const {value, setValue} = useContext(SortingContext);

  return (
    <Layout>
      <SortingContext.Consumer>
         {value =>
            <div className='main_screen'>
              <h1>{value}</h1>
            </div>
            {siteOptions && <Footer />}
         }
      </SortingContext.Consumer>
    </Layout>
  )
};

However, my recommendation would probably be to move it up higher, you could have it at the app level.

TheBird956
  • 293
  • 5
  • 14
Alex K
  • 832
  • 4
  • 8
  • 2
    I definitely understand what you mean, but I don't have an app.js file with nextjs. Apparently I have to create a new _app.js page to be able to accomplish what I'm looking for. Thanks for routing me in the right direction. – Carlos G Jun 09 '20 at 18:57
  • That's correct, but I think that makes sense, then if you have multiple contexts, you can place them there. – Alex K Jun 09 '20 at 19:13
0

if you want to use Context same store or Redux whole your project I hope this suggestion can help you:

Philosophy: you must create on object in your project to save your pagination and or your tab if user click on one post and send back to articles you must use your pagination in your store and this store when you refresh page can be reset please do same this:

create on Provider top of project root in the _app.js same this or you can create it in the top of articles component

<ConfigProvider>
   <your project/>
</ConfigProvider>

in the src your project you can create on directori Provider and import all of things on this Document and now create your store same this:

import { createContext, useContext } from "react";
import React, { useState } from "react";
export const ConfigContext = createContext();

function ConfigProvider({ children }) {
       const [config, setConfig] = useState({
          type: null,
          page: 1,
          tab: null,
          selectTab: null,
          Filter: {
              sort: "desc",
              type: null,
          },
   });
    return (
    <ConfigContext.Provider value={[config, setConfig]}>
            {children}
    </ConfigContext.Provider>
 );
}

export default ConfigProvider;

and on top you have on the object you can use the same store that's meaning config and setConfig if you want just a page you can use just on-page param on config variable and now how to use it in your project see below: first, import them from your directory in tour component same this:

import {
ConfigContext,
} from "../../providers/ConfigProvider";

and right now you have on store use in in your function component same this:

    const [config, setConfig] = React.useContext(ConfigContext);

now you can see your store with console same below:

console.log(config)

and how to set your pagination in your store you can set store simply with setConfig same below:

but be careful you must use the correct useEffect if your project to updates your state

   useEffect(()=> {
      setConfig({
         type: "your type",
         page: your pagination number,
         tab: "your custom tab if you want",
          etc...
       })
    },[your pagination change])

and after back from article, you can load your page same this with correct useEffect in your component

const [currentPage, setCurrentPage] = useState(1);
useEffect(()=>{
    setCorrectPage(config.page)
 },[])
Nicolas Bouvrette
  • 4,295
  • 1
  • 39
  • 53
Omid Moghadas
  • 341
  • 3
  • 8