I'm trying to provide my data via ContextProvider to my own reactComponent. I've create Context.jsx (I wanted to have external context file). But when I try to connect my Context.jsx with _app.jsx I have an arror:
Could not find a declaration file for module './Context.jsx'. 'Context.jsx' implicitly has an 'any' type.ts(7016)
And here below the code of my Context.jsx:
import React, { createContext, useState, useEffect, useContext } from "react";
const Context = createContext();
const Provider = ({ children }) => {
// the value that will be given to the context
const [code, setCode] = useState(null);
useEffect(() => {
const fetchBlogs = () => {
fetch(`https://node-test-mongo.herokuapp.com/api/blog`)
.then((response) => {
return response.json();
})
.then((data) => {
setCode(data.blogs)
})
.catch((error) => console.log("An error occured"));
};
fetchBlogs();
}, []);
// the Provider gives access to the context to its children
return <Context.Provider value={code}>{children}</Context.Provider>;
};
export const useCoder = () => useContext(Context);
export default Provider;
What the issue could be here?
Thank you in advance for help:)
Related
I created a Context object named AuthContext to hold user information on the application. After I get the user information with this Context object and do the necessary operations, I save the information with the AsnycStore and direct it to the Main.js page. but sometimes I need to change this information. I created a file named API/index.js and wrote a function that can re-login according to the user's status. when I run this function it will need to trigger a function under the AuthContext I created but I can't call the function in the AuthContext
AuthContext.js
import AsyncStorage from '#react-native-async-storage/async-storage';
import React, { createContext, useEffect, useState } from 'react';
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
//const [test, setTest] = useState("test tuta");
const [userToken, setUserToken] = useState(null);
const [userInfo, setUserInfo] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const [guest, setGuest] = useState(null)
const login = (userInfo) => {
setIsLoading(true);
setUserToken(userInfo.kullanici_id);
setUserInfo(userInfo);
AsyncStorage.setItem("userToken", JSON.stringify(userInfo.kullanici_id));
AsyncStorage.setItem("localUserInfo", JSON.stringify(userInfo));
setIsLoading(false)
}
const isGuest = () => {
setIsLoading(true);
setGuest(true);
AsyncStorage.setItem("guest", "true");
setIsLoading(false)
}
const test= ()=>{ //I will call this function in API/index.js
console.log("test log")
}
const logout = () => {
setIsLoading(true);
setUserToken(null);
setUserInfo(null);
setGuest(null);
AsyncStorage.removeItem("userToken");
AsyncStorage.removeItem("localUserInfo");
AsyncStorage.removeItem("guest")
setIsLoading(false)
}
const isLoggedIn = async () => {
try {
setIsLoading(true);
let userToken = await AsyncStorage.getItem("userToken");
setUserToken(userToken);
let userInfo = await AsyncStorage.getItem("localUserInfo");
setUserInfo(JSON.parse(userInfo));
console.log("------------- userlocal")
console.log(userInfo);
setIsLoading(false);
} catch (e) {
console.log("isLoggenIn error ${e}")
}
}
const isGuestIn = async () => {
try {
setIsLoading(true);
let guestToken = await AsyncStorage.getItem("guest");
setGuest(guestToken);
setIsLoading(false);
} catch (e) {
console.log("isLoggenIn error ${e}")
}
}
useEffect(() => {
isLoggedIn(),
isGuestIn()
}, [])
return (
<AuthContext.Provider value={{ login, logout, isLoading, userToken, guest, isGuest,userInfo,deneme }}>
{children}
</AuthContext.Provider>
)
}
API/index.js
import AsyncStorage from "#react-native-async-storage/async-storage";
import axios from "axios";
import { useContext } from "react";
import { BASE_URL } from "../config";
import { AuthContext,AuthProvider } from "../context/AuthContext";
export const oturumKontrol = async () => {
const { test} = useContext(AuthContext);
test(); //Im using test function inside AuthContext
console.log("oturum kontrol")
}
Error
Possible Unhandled Promise Rejection (id: 0):
Error: 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
How can I access the function inside the AuthContext?
You cannot use hooks in normal functions. They must be used in functional components at top level.
You can pass the function from the hook as an argument to another function and use it like that.
//SomeComponent where you want to call oturumKontrol
const { test} = useContext(AuthContext); // get function
//call this on press or in useEffect
const handleTest = async () => {
await oturumKontrol(test) //pass function
}
export const oturumKontrol = async (test) => {
test(); //call function
console.log("oturum kontrol")
}
Make sure that you pass test function in your AuthContext first
I've built a code at codesandbox so I could display what is going on.
When I uncomment the <p>{chosenPokemon[0].id}</p> line in the aplication run a error: Cannot read properties of undefined (reading 'id'). Here is the file where the problem is going on:
import { usePokemon } from "../../hooks/usePokemon";
export const MapTest = () => {
const { chosenPokemon } = usePokemon("ditto");
console.log(chosenPokemon);
return (
<>
<h1>Map Test</h1>
{/* <p>{chosenPokemon[0].id}</p> */}
</>
);
};
My conclusion is that my context file is bringing a undefined value before bringing the correct one. Here is my context file:
import { createContext, useContext, useState, useEffect } from "react";
import axios from "axios";
const DittoContext = createContext();
export default function DittoProvider({ children }) {
const [ditto, setDitto] = useState();
const api = axios.create({
baseURL: "https://pokeapi.co/api/v2/pokemon"
});
useEffect(() => {
api
.get("/ditto")
.then((response) => setDitto(response.data))
.catch((err) => console.log(err));
}, []);
return (
<DittoContext.Provider value={{ ditto, setDitto }}>
{children}
</DittoContext.Provider>
);
}
export const useDitto = () => {
const context = useContext(DittoContext);
const { ditto, setDitto } = context;
return { ditto, setDitto };
};
How can I fix this error that has been going on?
I have an existing context for products. Where initially I used some mock data as shown below STORE_DATA to render the components. Now I need to replace that mock data and connect to a Node.js api which is available on my local port (created the api I after I created the react-app).
import React, { createContext, useState } from 'react';
import STORE_DATA from '../shop';
export const ProductsContext = createContext();
const ProductsContextProvider = ({ children }) => {
const [products] = useState(STORE_DATA);
return (
<ProductsContext.Provider value={{ products }}>
{
children
}
</ProductsContext.Provider>
);
}
export default ProductsContextProvider;
Just created a helper.js file witht he following to fetch the data:
import {useEffect} from "react";
const fetchData = () => {
return fetch("https://localhost:8081/products") <<tested on postman and works fine.
.then((response) => response.json())
.then((data) => console.log('Fetching Data:',data));
}
How to replace the mock data on the context file and use this fetchData() using useEffect within the context? What code should change?
Tried the following, but didn't work, can't even print the console.log:
import React, { createContext, useState, useEffect } from 'react';
import { fetchData } from '../helpers';
export const ProductsContext = createContext();
const ProductsContextProvider = ({ children }) => {
const [products, setProducts] = useState(null);
useEffect(() => {
setProducts(fetchData());
}, []);
return (
<ProductsContext.Provider value={{ products }}>
{
children
}
</ProductsContext.Provider>
);
}
export default ProductsContextProvider;
The issue was that it was returning the following error (explained):
net::ERR_SSL_PROTOCOL_ERROR (on chrome)
Solution: Use http:// instead of https:// in the URL's in the following code:
const fetchData = () => {
return fetch("http://localhost:8081/products")
.then((response) => response.json())
.then((data) => console.log('Fetching Data:',data));
}
For testing purposes, I setup a dependency injection on my react component. I'm not sure if it's the best practice.
I got this warning React Hook useEffect has a missing dependency:
Is there a better way to fix or to make my intent?
In my basic example, I have a service that fetch an "hello world" on an Api. My component use the service to fetch the data on loading.
I can easly test my component thanks to the dependency injection (with props), by inject some mock function.
## hello-world.js
import React, {useEffect, useState} from "react";
import {fetchHelloWorld} from "../services/fetch-hello-world";
import PropTypes from 'prop-types';
const HelloWorld = ({
fetchHelloWorld
}) => {
const [message, setMessage] = useState('');
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchHelloWorld().then(message => {
setIsLoading(false);
setMessage(message.response);
})
}, [fetchHelloWorld]
);
return (
<>
{isLoading ? "Loading" : message}
</>
);
}
HelloWorld.defaultProps = {
fetchHelloWorld: fetchHelloWorld
}
HelloWorld.propTypes = {
fetchHelloWorld: PropTypes.func.isRequired
}
export default HelloWorld;
## fetch-hello-world.js
export function fetchHelloWorld() {
return fetch("/controller/hello_world")
.then(res => res.json())
.catch(e => console.error(e));
}
in this way i can test my component like that :
import React from "react";
import {act, render, screen} from "#testing-library/react";
import HelloWorld from "./hello-world";
describe("Hello Wolrd", () => {
test('should display Loading when data not load', async () => {
render(<HelloWorld/>);
const linkElement = screen.getByText('Loading');
expect(linkElement).toBeInTheDocument();
});
test('should display data when loaded', async () => {
let fakeFetchHelloWorld = () => Promise.resolve({response: "Hello World"});
await act(async () => {
render(<HelloWorld fetchHelloWorld={fakeFetchHelloWorld}/>);
})
const linkElement = screen.getByText('Hello World');
expect(linkElement).toBeInTheDocument();
});
})
im trying to create an api request with the header value, that is received from a context component. However, as soon as the page component is loaded, it throws an Cannot read property '_id' of null exception. Is there a way to run the useEffect function, as soon as the context is loaded?
main component:
import React, { useState, useEffect, useContext } from "react";
import "./overview.scss";
/* COMPONENTS */;
import axios from 'axios';
import { GlobalContext } from '../../components/context/global';
const Overview = () => {
const [bookings, setBookings] = useState([]);
const [loaded, setLoaded] = useState(false);
const [user, setUser] = useContext(GlobalContext);
useEffect(() => {
axios
.get(`/api/v1/bookings/user/${user._id}`)
.then(res => setBookings(res.data))
.catch(err => console.log(err))
.finally(() => setLoaded(true));
}, [user]);
context component:
import React, {useState, useEffect, createContext} from 'react';
import jwt from 'jsonwebtoken';
/* GLOBAL VARIABLES (CLIENT) */
export const GlobalContext = createContext();
export const GlobalProvider = props => {
/* ENVIRONMENT API URL */
const [user, setUser] = useState([]);
useEffect(() => {
const getSession = async () => {
const user = await sessionStorage.getItem('authorization');
setUser(jwt.decode(user));
}
getSession();
}, [])
return (
<GlobalContext.Provider value={[user, setUser]}>
{props.children}
</GlobalContext.Provider>
);
};
The issue here is useEffect is running on mount, and you don't have a user yet. You just need to protect against this scenario
useEffect(() => {
if (!user) return;
// use user._id
},[user])
Naturally, when the Context fetches the user it should force a re-render of your component, and naturally useEffect should re-run as the dependency has changed.
put a condition before rendering you GlobalProvider, for example:
return (
{user.length&&<GlobalContext.Provider value={[user, setUser]}>
{props.children}
</GlobalContext.Provider>}
);
If user is not an array just use this
return (
{user&&<GlobalContext.Provider value={[user, setUser]}>
{props.children}
</GlobalContext.Provider>}
);