1

I'm struggling with providing data via useContext. I know how to create useContext in React, but I was trying several times to do the same in Next.js with TypeScript.

Could someone please help me. Here below my _app.jsx code:

import { AppProps } from 'next/app';
import Head from 'next/head';
import React, { useState } from 'react';
import '../styles/globals.css';

import { CodeContextProvider } from '../shared/context/Context.jsx'


function MyApp({ Component, pageProps }: AppProps): JSX.Element {
    const [context, setContext] = useState("Kyiv");
    return (
        <CodeContextProvider>
            <Head>
                <title></title>
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <Component {...pageProps} />
        </CodeContextProvider>
    )
}

export default MyApp;

My plan is to get data from my backend node.js (already deployed on heroku server). I've tried to do that with useEffect in useContext external file, but... lots of different errors because of TypeScript.

here below my Context.jsx file:



import React, { createContext, useState, useEffect } from "react";

// create context
const CodeContext = createContext();

const CodeContextProvider = ({ children }) => {
  // the value that will be given to the context
  const [blog, setBlogs] = useState(null);

  // fetch a user from a fake backend API
  useEffect(() => {
    const fetchData = () => {
      // this would usually be your own backend, or localStorage
      // for example
      fetch(`https://node-test-mongo.herokuapp.com/api/blog`)

      .then((response) => {
          return response.json();
      })
      .then((data) => {
          setBlogs(data.blogs)
      })
    };
    
    fetchData().catch(console.error);
  }, []);

  return (
    // the Provider gives access to the context to its children
    <CodeContext.Provider value={blog}>
      {children}
    </CodeContext.Provider>
  );
};

export { CodeContext, CodeContextProvider };


I just need have data (title and text) from my api and to have posibility to take it everywhere I want.

Thanks in advance. I'll be really appreciate for help:)

  • 1
    hi not sure if this might be of interest https://stackoverflow.com/questions/68120049/nextjs-typescript-usecontext-through-the-pages – jspcal Aug 10 '22 at 17:11
  • 1
    What is the problem? There's a few oddities in there like `UserContext.Provider value={user}` (where is user defined), but I think it's probably paste errors. You also define local `[context, setContext]` state in `App` but you do nothing with it. You also do not ever _use_ the `UserContextProvider` that you created. You would want to wrap your component in `App.tsx` with the provider.. – Yuji 'Tomita' Tomita Aug 10 '22 at 21:00
  • Yes, but I've deleted lots of things and tried lots of ways and tutorials, this is the reason why I'm asking for help here – user17880509 Aug 10 '22 at 21:08
  • 1
    Is it important for you to use context ? Because your example is a perfect use case for React Query or SWR. With this libs, your component can fetch external data and re renders only when data is ready. – pom421 Aug 10 '22 at 21:46
  • 1
    @user17880509 ok now use a consumer and you're good. `const blogs = useContext(MyContext)` – Yuji 'Tomita' Tomita Aug 11 '22 at 05:02
  • I just want to learn how to use Context in Next.js with TS. And now it says me, that my fetchData( ) is undefined... I don't even know why. @pom421, React Query or SWR I'll better try after getting rid of useContext. As I wrote I'm a newbie :) – user17880509 Aug 11 '22 at 06:23
  • The reason why useContext, because I have my input and list elements in different components:) – user17880509 Aug 11 '22 at 06:29

1 Answers1

0

Allright thanks to @pom421 I've solved the issue. I still don't know how to use Context with TS, but now I know ho to do that with React Query.

First of all use npm i react-query@3 to install 3-rd version of the library. Below code won't work with 4+ version

Here below my _app.tsx code:

import { AppProps } from 'next/app';
import Head from 'next/head';
import React, { useState } from 'react';
import '../styles/globals.css';
import { QueryClient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            //refetchOnWindowFocus: false //disable refetch everywhere when change page
        },
    },
});

function MyApp({ Component, pageProps }: AppProps): JSX.Element {
    return (
        <QueryClientProvider client={queryClient}>
            <Head>
                <title></title>
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <Component {...pageProps} />
        </QueryClientProvider>
    )
}

export default MyApp;

As you see, you just need to install the library, wrap your code on </QueryClientProvider> and import service (custom file for such cases).

Here below code of my app.service.ts

import axios from "axios"

const API_URL = 'https://node-test-mongo.herokuapp.com'

axios.defaults.baseURL = API_URL

export const CryptoService = {
    async getAll() {
        return axios.get('/api/blog')
    }
}

Now we can use our query from everywhere inside our project (like useContext).

Here below my custom <List.tsx /> component`s code:

import { ListProps } from "./List.props";
import styles from "./List.module.css";
import { P } from '../'
import React, { useEffect, useState } from "react";
import { useQuery } from "react-query";
import { CryptoService } from "../../app/services/crypto.service";

export const List = ({ children, className, ...props }: ListProps): JSX.Element => {

    const [blogs, setBlogs] = useState<any[]>([]);

    const { isLoading, data: response, isFetching } = useQuery('crypto', () => CryptoService.getAll())

    return (
        <div

            className={styles.ul}
            {...props}
        >   

            {isLoading ? (
                <div>Loading ...</div>
            ) : response?.data.blogs.length ? (
                <div>
                    {response.data.blogs.map((blog: any) => (
                        <ul key={blog._id}>
                            <li className={styles.li} >
                                <P className={styles.title} size='l'>{blog.title}</P>
                                <P size='l'>{blog.text} </P>
                            </li>
                        </ul>
                    ))}
                    {isFetching && <div className="mt-5">Loading data ...</div>}
                </div>
            ) : (<div>Elements not found ...</div>)}
        </div>
    )
};

As you see I'm not using any useEffect or useContext with states, so my application definitely doesn't know when I add new element to my array on backend ...

To solve this, you just could add something like that:

import { useQuery } from "react-query";
import { CryptoService } from "../../app/services/crypto.service";

export const Search = ({ className, ...props }: SearchProps): JSX.Element => {


    const { refetch, isFetching } = useQuery('crypto', () => CryptoService.getAll())

    const sendRequest = async () => {
        if (isEncode) {
            const res = await axios.post(`https://node-test-mongo.herokuapp.com/api/blog/encodepost`, {
                title: "Robohamster",
                text: "Its a secret text",
                secretkeyword: "gravityfalls",
            }).catch(err => console.log(err));
        }
        if (!isEncode) {
            const res = await axios.post(`https://node-test-mongo.herokuapp.com/api/blog/decodepost`, {
                text: "fefe",
                secretkeyword: "asfsef",
            }).catch(err => console.log(err));
        }
        console.log("here", inputs);
        refetch(); //this for refetch data 
    }
    return (
        
            <Button
                appearance="ghost"
                className={styles.button}
                onClick={() => sendRequest()}
            >
                Send                //this will send and refresh automatically
            </Button>
    )

}


So, refetch() is for refetching data from server like useEffect[]

Hope I helped you. Good luck:)