So I have set two datepickers which needs to be reformatted to MM-YYYY which I done with the following code:
const [dateFrom, setDateFrom ] = useState('2012-01-01')
const [dateTill, setDateTill ] = useState('2012-12-31')
const [yearFrom, monthFrom, dayFrom] = dateFrom.split('-');
const newDateFrom = `${monthFrom}-${yearFrom}`
const [yearTill, monthTill, dayTill] = dateTill.split('-');
const newDateTill = `${monthTill}-${yearTill}`
export default {newDateFrom, newDateTill}
I want to use the newDateFrom and newDateTill variables in my api file located in my api folder. I have used string interpolation here.
export const fetchTimeData = async () => {
const response = await fetch(`https://api.punkapi.com/v2/beers?brewed_before=${newDateTill}&brewed_after=${newDateFrom}&per_page=80`)
const data = await response.json();
return data
}
How can I get this to work? I'm not sure if props would work as there isn't a parent-child relationship going on here?
Thank you
You can't call the useState function in the global scope; if you want the variables returned from useState to be accessible to other functions, you need to make a custom hook like the following:
export function useNewDate() {
const [dateFrom, setDateFrom ] = useState('2012-01-01')
const [dateTill, setDateTill ] = useState('2012-12-31')
const [yearFrom, monthFrom, dayFrom] = dateFrom.split('-');
const newDateFrom = `${monthFrom}-${yearFrom}`
const [yearTill, monthTill, dayTill] = dateTill.split('-');
const newDateTill = `${monthTill}-${yearTill}`
return {newDateFrom, newDateTill}
}
Then, because you're using hooks, your fetchTimeData also needs to be a hook:
import { useNewDate } from './useNewDate' // or whichever file your useNewDate hook is stored in
import { useState, useEffect } from 'react';
export const useFetchTimeData = () => {
const { newDateFrom, newDateTill } = useNewDate()
const [timeData, setTimeData] = useState(null)
useEffect(() => {
(async () => {
const response = await fetch(`https://api.punkapi.com/v2/beers?brewed_before=${newDateTill}&brewed_after=${newDateFrom}&per_page=80`)
const data = await response.json();
setTimeData(data)
})();
}, [])
return timeData
}
Then, whenever you want to fetch the time data from within a React component, you would import this hook:
import { useFetchTimeData } from './useFetchTimeData'
export function MyComponent() {
const timeData = useFetchTimeData();
if (timeData === null) {
return <div>Loading time data...</div>
}
return <div>{JSON.stringify(timeData)}</div>
}
Related
I just started to learn React and was trying to fetch some random data. i created a useState and have two values : const [name, setName] = useState([]);
when i try to do name : response.json();
I get an error that assignment to a constant variable, I'm following a tutorial which is doing the same.
surprisingly when I create a constant variable with a different name, it works. I only can't assign the name = await response.json();
Thank you
import React, { useEffect, useState } from "react";
import { ReactDOM } from "react";
const FetchData = () =>{
const [name, setName] = useState([]);
const fetchNames = async () =>{
const url = `https://randomuser.me/api`;
try {
const response = await fetch(url);
name = await response.json();
console.log(name);
setName(name);
} catch (error) {
console.log(error);
}
}
useEffect(()=>{
fetchNames();
},[])
return(
<div>
</div>
);
}
export default FetchData;
Check my example, as I understand you want to use name inside the try as variable not from state so you should add const. I also mapped the response in your render which you can see the results. Here also codesandbox example https://codesandbox.io/embed/friendly-ishizaka-57i66u?fontsize=14&hidenavigation=1&theme=dark
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [name, setName] = useState([]);
const fetchNames = async () => {
const url = `https://randomuser.me/api`;
try {
const response = await fetch(url);
constname = await response.json();
console.log(name);
setName(name?.results);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
fetchNames();
}, []);
return (
<div>
{name.map((el) => (
<>
<p>{el.email}</p>
<p>{el.phone}</p>
</>
))}
</div>
);
}
there are 2 things that might help with this confusion
any variable created using const will forever hold the value it had when it was declared, so for eg if we have const x = "😀", we later can't do something like x = "something else"
in the snippet you shared name is a "state variable". state variables should NEVER be updated directly, state updates must happen through setState function only(i.e. setName in this case)
this is the part in official docs that talks about it https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly , https://reactjs.org/docs/hooks-state.html#updating-state
I have a custom react hook that is fetching data from an api. I'm getting error: "Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component."
I don't know if this is due to the structure of my custom hook or how I'm using the hook in the component where it's being called.
Here is my custom hook:
import { useState, useEffect } from "react";
const axios = require('axios');
const crypto = require('crypto');
function useFetchNGSData(endpoint) {
const[data, setData] = useState(null);
const dateString = () => {
// MORE CODE
return date.toString();
}
const ngs_username = process.env.REACT_APP_NGS_USERNAME;
const ngs_password = process.env.REACT_APP_NGS_PASSWORD;
const ngs_access_token = process.env.REACT_APP_NGS_ACCESS_TOKEN;
const ngs_secret_key = process.env.REACT_APP_NGS_SECRET_KEY;
const string_to_sign = ngs_username + ngs_password + ngs_access_token + dateString();
const digest = crypto.createHmac('sha1', ngs_secret_key).update(string_to_sign).digest('base64');
const authKey = 'NGS ' + ngs_access_token + ':' + digest;
const url = 'https://api.ngs.com';
useEffect(() => {
axios.defaults.headers.common['Authorization'] = authKey;
axios.get(url + endpoint)
.then((response) => {
setData(response.data);
})
.catch((error) => {
console.log(error);
})
},[endpoint]);
return { data };
}
export default useFetchNGSData;
Here's the component that is using the custom hook:
import React, { useState } from 'react';
import useFetchNGSData from '../useFetchNGSData';
const Data = (props) => {
const [players, setPlayers] = useState([]);
const handleGetPlayers = () => {
teamCollection.forEach(teamId => {
setPlayers(useFetchNGSData('/league/roster/current?teamId=5100')
});
}
return (
<div>
// ... handleGetPlayers
</div>
);
}
export default Data;
I know I'm omitting a lot of code but hopefully this is enough to demonstrate the issue.
You need to move the hook out of the event handler, and in to the body of the component.
import React, { useState } from 'react';
import useFetchNGSData from '../useFetchNGSData';
const Data = (props) => {
const { data: players } = useFetchNGSData('/league/roster/current?teamId=5100')
return (
<div>
// ... handleGetPlayers
</div>
);
}
export default Data;
But really, you don't need to use setPlayers here. You already have that data in your returned values from your custom hook.
I'm using next with typescript and trying to load a webworker.
For some reason - when I'm trying to initialize the webworker when I'm creating a hook - next tells me that Worker is not defined.
I'm using comlink but I suspect it's an issue with Next since comlink isn't in the mix at the point of error.
You can find the problematic line in below code snippet useWorker.hooks.ts in the function makeWorkerApiAndCleanup
Does anyone understand why this is happening?
const worker = new Worker(url);
useWorker.hooks.ts
import { wrap, releaseProxy } from "comlink";
import { useEffect, useState, useMemo } from "react";
function makeWorkerApiAndCleanup() {
const url = new URL("./useWorker.worker", import.meta.url)
const worker = new Worker(url);
const workerApi = wrap<import("./useWorker.worker").WorkerType>(worker);
const cleanup = () => {
workerApi[releaseProxy]();
worker.terminate();
};
const workerApiAndCleanup = { workerApi, cleanup };
return workerApiAndCleanup;
}
function useWorker() {
const workerApiAndCleanup = useMemo(() => makeWorkerApiAndCleanup(), []);
useEffect(() => {
const { cleanup } = workerApiAndCleanup;
return () => {
cleanup();
};
}, [workerApiAndCleanup]);
return workerApiAndCleanup;
}
function useImplementation() {
const [data, setData] = useState({});
const { workerApi } = useWorker();
useEffect(() => {
workerApi.workerFunction();
setData({});
}, [workerApi]);
return data;
}
export default useImplementation;
useWorker.worker.ts
import { expose } from 'comlink';
import { workerFunction } from './functions';
const worker = {
workerFunction,
};
export type WorkerType = typeof worker;
expose(worker);
useWorker/index.ts
import useWorker from './useWorker.hooks';
export { useWorker };
We have a short question for our application (NextJS 11.0.0 + next-translate 1.0.7)
The library contains a function to make an API call (/lib/mylib.js) :
export const getDataExample = async (lang) => {
return fetch(_apiurl_/example/{lang});
};
And my component in react (/components/myComponent.js) call this function with a useEffect:
import { useEffect, useState } from 'react';
import useTranslation from 'next-translate/useTranslation';
import { getDataExample } from '/lib/mylib';
export default function MyComponent() {
const [data, setData] = useState(false);
const { lang } = useTranslation();
useEffect(() => {
const fetchData = async () => {
const response = await getDataExample(lang);
setData(response);
};
fetchData();
}, []);
[...]
}
I don't want to call getDataExample() directly with the lang parameter.
Is it possible to get the current language in the function (/lib/mylib.js) ?
Thank you for your reply !
But now imagine that my library (/lib/mylib.js) is also used to fetch data into a getServerSideProps :
export async function getServerSideProps({ locale }) {
const response = await getDataExample(locale);
[...]
}
React Hooks are not available here, so what do you do ?
You can create your custom hook. This is an example:
const useFetchWithLang = (func) => {
const { lang } = useTranslation()
return useCallback((args) => func({ ...args, lang }), [lang])
}
const fetchDataExample = ({ otherParam, lang }) => {
return { test: 'test1' }
}
const fetchDataExampleWithLang = useFetchWithLang(fetchDataExample)
After for example, you could use it in a useEffect.
useEffect(() => {
const fetchData = async () => {
const response = await fetchDataExampleWithLang({ otherParam: 'test' });
setData(response);
};
fetchData();
}, []);
I have a React Native App,
Here i use mobx ("mobx-react": "^6.1.8") and react hooks.
i get the error:
Invalid hook call. Hooks can only be called inside of the body of a function component
Stores index.js
import { useContext } from "react";
import UserStore from "./UserStore";
import SettingsStore from "./SettingsStore";
const useStore = () => {
return {
UserStore: useContext(UserStore),
SettingsStore: useContext(SettingsStore),
};
};
export default useStore;
helper.js OLD
import React from "react";
import useStores from "../stores";
export const useLoadAsyncProfileDependencies = userID => {
const { ExamsStore, UserStore, CTAStore, AnswersStore } = useStores();
const [user, setUser] = useState({});
const [ctas, setCtas] = useState([]);
const [answers, setAnswers] = useState([]);
useEffect(() => {
if (userID) {
(async () => {
const user = await UserStore.initUser();
UserStore.user = user;
setUser(user);
})();
(async () => {
const ctas = await CTAStore.getAllCTAS(userID);
CTAStore.ctas = ctas;
setCtas(ctas);
})();
(async () => {
const answers = await AnswersStore.getAllAnswers(userID);
UserStore.user.answers = answers.items;
AnswersStore.answers = answers.items;
ExamsStore.initExams(answers.items);
setAnswers(answers.items);
})();
}
}, [userID]);
};
Screen
import React, { useEffect, useState, useRef } from "react";
import {
View,
Dimensions,
SafeAreaView,
ScrollView,
StyleSheet
} from "react-native";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from "react-native-responsive-screen";
import { observer } from "mobx-react";
import useStores from "../../stores";
import { useLoadAsyncProfileDependencies } from "../../helper/app";
const windowWidth = Dimensions.get("window").width;
export default observer(({ navigation }) => {
const {
UserStore,
ExamsStore,
CTAStore,
InternetConnectionStore
} = useStores();
const scrollViewRef = useRef();
const [currentSlide, setCurrentSlide] = useState(0);
useEffect(() => {
if (InternetConnectionStore.isOffline) {
return;
}
Tracking.trackEvent("opensScreen", { name: "Challenges" });
useLoadAsyncProfileDependencies(UserStore.userID);
}, []);
React.useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
CTAStore.popBadget(BadgetNames.ChallengesTab);
});
return unsubscribe;
}, [navigation]);
async function refresh() {
const user = await UserStore.initUser(); //wird das gebarucht?
useLoadAsyncProfileDependencies(UserStore.userID);
if (user) {
InternetConnectionStore.isOffline = false;
}
}
const name = UserStore.name;
return (
<SafeAreaView style={styles.container} forceInset={{ top: "always" }}>
</SafeAreaView>
);
});
so now, when i call the useLoadAsyncProfileDependencies function, i get this error.
The Problem is that i call useStores in helper.js
so when i pass the Stores from the Screen to the helper it is working.
export const loadAsyncProfileDependencies = async ({
ExamsStore,
UserStore,
CTAStore,
AnswersStore
}) => {
const userID = UserStore.userID;
if (userID) {
UserStore.initUser().then(user => {
UserStore.user = user;
});
CTAStore.getAllCTAS(userID).then(ctas => {
console.log("test", ctas);
CTAStore.ctas = ctas;
});
AnswersStore.getAllAnswers(userID).then(answers => {
AnswersStore.answers = answers.items;
ExamsStore.initExams(answers.items);
});
}
};
Is there a better way? instead passing the Stores.
So that i can use this function in functions?
As the error says, you can only use hooks inside the root of a functional component, and your useLoadAsyncProfileDependencies is technically a custom hook so you cant use it inside a class component.
https://reactjs.org/warnings/invalid-hook-call-warning.html
EDIT: Well after showing the code for app.js, as mentioned, hook calls can only be done top level from a function component or the root of a custom hook. You need to rewire your code to use custom hooks.
SEE THIS: https://reactjs.org/docs/hooks-rules.html
You should return the value for _handleAppStateChange so your useEffect's the value as a depdendency in your root component would work properly as intended which is should run only if value has changed. You also need to rewrite that as a custom hook so you can call hooks inside.
doTasksEveryTimeWhenAppWillOpenFromBackgorund and doTasksEveryTimeWhenAppGoesToBackgorund should also be written as a custom hook so you can call useLoadAsyncProfileDependencies inside.
write those hooks in a functional way so you are isolating specific tasks and chain hooks as you wish without violiating the rules of hooks. Something like this:
const useGetMyData = (params) => {
const [data, setData] = useState()
useEffect(() => {
(async () => {
const apiData = await myApiCall(params)
setData(apiData)
})()
}, [params])
return data
}
Then you can call that custom hook as you wish without violation like:
const useShouldGetData = (should, params) => {
if (should) {
return useGetMyData()
}
return null
}
const myApp = () => {
const myData = useShouldGetData(true, {id: 1})
return (
<div>
{JSON.stringify(myData)}
</div>
)
}