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:)
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:)