I'm using react query to fetch data to the component.i have a requiremnt to do only first time the component renders.(Set a Default value)So I thought to use fetch data of the useQuery hook inside a useEffect.But it didn't work.
Here is my code...
export default function Header() {
.
.
.
//Here i'm using a custom hook
const { data: workSpaceArray } = useWorkspacesByUserData(onFetchWorkspaceSuccess, onFetchWorkspaceError);
useEffect(() => {
const defaultWorkspaceItem = workSpaceArray?.data.info.workspaces.filter(workspace => workspace.name === workSpaceArray?.data.info.default_workspace_id);
localStorage.removeItem(USER_WORKSPACE);
localStorage.setItem(USER_WORKSPACE, JSON.stringify(defaultWorkspaceItem[0]));
setSelectedWorkspaceValue(defaultWorkspaceItem[0].name)
}, []);
return(
...
);
}
Inside the custom hook...
import { useQuery, useMutation } from 'react-query'
import { protectedRequest } from '../utils/axios-utils'
import { ResponseHandler } from "./ResponseHandler"
const fetchWorkspacesByUserId = async () => {
const response = await protectedRequest({ url: '/workspace/load' });
return ResponseHandler(response);
}
export const useWorkspacesByUserData = (onSuccess, onError) => {
return useQuery('fetch-workspaces-by-user', fetchWorkspacesByUserId,
{
onSuccess,
onError,
staleTime: 120000,
})
}
The error I'm getting.....
Can anybody give me help to fix this issue? Thank you!
Thank you all who looked into this problem. I was able to figure it out by using refetch inside useEffect.
export default function Header() {
.
.
.
//Here i'm using a custom hook
const { data: workSpaceArray, refetch } = useWorkspacesByUserData(onFetchWorkspaceSuccess, onFetchWorkspaceError);
useEffect(() => {
refetch();
const defaultWorkspaceItem = workSpaceArray?.data.info.workspaces.filter(workspace => workspace.name === workSpaceArray?.data.info.default_workspace_id);
localStorage.removeItem(USER_WORKSPACE);
localStorage.setItem(USER_WORKSPACE, JSON.stringify(defaultWorkspaceItem[0]));
setSelectedWorkspaceValue(defaultWorkspaceItem[0].name)
}, []);
return(
...
);
}
Related
I've been struggling with this one for 2-3 days and hope someone can help. I am moving a blog project over from React to Next, and in one particular case a setState function isn't working.
The code below lives in the _app.tsx function at the top of my project. The editPost function is called from a button in a child component. The code pulls the selected blog post from the database then updates the state of postToEdit. This data is meant to be injected into an edit form via props-- and works fine in the React version of the blog.
In this case, the setState (setPostToEdit) function seems to do nothing. In the console.log function after setPostToEdit(newPostToEdit), you can see that the data has been pulled from Postgres correctly, but the state doesn't change.
In the deletePost and getPosts function in this same _app component, everything works fine. Weird! Any help sincerely appreciated, I'm new to both React and Next.
import '../styles/globals.css'
import React, { useState, useEffect } from 'react'
import type { AppProps } from 'next/app'
import Layout from '../components/Layout'
export default function App({ Component, pageProps }: AppProps) {
const initPostToEdit = {
post_id: '',
title: 'initial post title',
sub_title: '',
main_content: '',
post_url: 'initial URL',
page_title: '',
meta_description: '',
meta_keywords: ''
}
const [posts, setPosts] = useState([]);
const [postToEdit, setPostToEdit] = useState(initPostToEdit);
const [blogValues, setBlogValues] = useState(initPostToEdit);
const deletePost = async (id) => {
try {
await fetch(`http://localhost:5001/blog-edit/${id}`, {
method: "DELETE"
})
setPosts(posts.filter(post => post.post_id !== id))
} catch (error) {
console.error(error.message)
}
}
const editPost = async (id) => {
try {
const response = await fetch(`http://localhost:5001/blog-edit/${id}`, {
method: "GET"
})
const newPostToEdit = await response.json()
setPostToEdit(newPostToEdit)
console.log('postToEdit:', newPostToEdit[0], postToEdit)
window.location.assign("/admin/blog-edit");
} catch (error) {
console.error(error.message)
}
}
const getPosts = async () => {
try {
const response = await fetch("http://localhost:5001/blog-edit");
const jsonData = await response.json();
setPosts(jsonData);
} catch (error) {
console.error(error.message)
}
}
useEffect(() => { getPosts(); }, [])
return (
<div>
<Layout>
<Component
{...pageProps}
editPost={editPost}
postToEdit={postToEdit}
setPostToEdit={setPostToEdit}
blogValues={blogValues}
setBlogValues={setBlogValues}
posts={posts}
deletePost={deletePost}
/>
</Layout>
</div>
)
}
const getStaticProps is for fetching API, I want to run this with:
useEffect(()=>{
getStaticProps()
})
but I get this error:
ReferenceError: data is not defined
What is the correct way to run "const getStaticProps" with "useEffect"?
import React, { useEffect, useState } from 'react';
export const getStaticProps = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/photos');
const data = await res.json();
var i = 0;
return {
props: { test: data.slice(0, (i += 10)) },
};
};
function home({ test }) {
useEffect(() => {
getStaticProps();
});
return <h1>h1</h1>;
}
export default home;
If you meant nextjs getStaticProps method, there is no way to use both together. That method is to fetch data on server side. But useEffect will be fired after component rendering (client side).
You can await for the promise to settled and take value
CODESANDBOX LINK
import "./styles.css";
import React, { useEffect, useState } from "react";
export const getStaticProps = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/photos");
const data = await res.json();
var i = 0;
return {
props: { test: data.slice(0, (i += 10)) }
};
};
function Home({ test }) {
useEffect(() => {
async function fn() {
const result = await getStaticProps();
// Now you can use value result
console.log(result);
}
fn();
}, []);
return <h1>h1</h1>;
}
export default function App() {
return (
<div>
<Home />
</div>
);
}
As I can see, you probably want to use a variable to pass it to the view. If so, you need useState hook. https://reactjs.org/docs/hooks-state.html
Here's could be a possible solution:
import React, { useEffect, useState } from 'react';
function Home({ test }) {
const [data, setData] = useState([]);
const getStaticProps = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/photos');
const data = await res.json();
setData(data);
};
useEffect(() => {
getStaticProps();
});
return <h1>h1</h1>;
}
export default Home;
we can directly fetch the response using .then from promise in useffect
useEffect(() => {
getStaticProps().then((res) => {
console.log(res.props.test);
});
For getStaticProps no Need to call it. when you export function with name of 'getStaticProps' next automatically call it before component mount.
import React, { useEffect, useState } from 'react';
function Home({ test }) {
console.log(test);
return <h1>h1</h1>;
}
export async function getStaticProps({ params }) {
const res = await fetch('https://jsonplaceholder.typicode.com/photos');
const data = await res.json();
var i = 0;
return {
props: { test: data.slice(0, (i += 10)) },
};
};
export default Home;
I am trying to get data from the user but it always returns null, the problem here that the return function runs first so the getUser is null how can i render the useEffect before it returns the provider ?
Here is my code :
import React, { useState, useEffect } from 'react';
import axios from '../axios'
export const UserContexts = React.createContext();
const UserContext = ({children}) => {
const [getUser, setGetUser] = useState(null);
useEffect(async () => {
try {
const { data } = await axios.post("/check",{}, { withCredentials: true });
setGetUser(data);
console.log("i render second")
console.log(data);
} catch (error) {
console.log(error);
}
}, [])
const { Provider } = UserContexts;
return (
<Provider value={getUser} >
{console.log("i render first ")}
{children}
</Provider>
)
}
export default UserContext
and here is the output :
the problem here that it renders Provider then the useContext that i am trying to get the data then it calls the useEffect to get the data then returns it , how can i call useEffect before all of this ?
You can do this outside of the context, in the root of the same file:
const userPromise = axios.post("/check",{}, { withCredentials: true })
Then in useEffect do
useEffect(async () => {
try {
const { data } = await userPromise;
setGetUser(data);
console.log("i render second")
console.log(data);
} catch (error) {
console.log(error);
}
}, [])
It will run the request at the start of the app but also would sync well with your context, correctly handling absent and loaded states
If you want to wait on it until the value is there, you could try
getUser ? <Provider value={getUser} >
{console.log("i render first ")}
{children}
</Provider> : null
I wrote a function to make an API call. Typically, I'd just wrap it in a useEffect and throw it in the same file that needs it, but I'm trying to write my code a little cleaner. So I did the following.
In my component.js file, I have the following:
import { apiCall } from '../../../framework/api.js';
import { useEffect, useState } from 'react';
export const Table = () => {
const [ resp, setResp ] = useState();
useEffect(() => {
console.log(apiCall());
}, []);
return(
<>
{ resp &&
resp.map(([key, value]) => {
console.log("key: " + key);
return(
<SomeComponent />
);
})
}
</>
);
}
in my api.js file, I have the following:
import axios from 'axios';
import { useState } from 'react';
export const apiCall = () => {
const [ resp, setResp ] = useState();
axios.get('https://some.domain/api/get').then((response) => {
setResp(response.data);
});
if(resp) return resp;
}
This always returns an error (Invalid hook call. Hook calls can only be called inside the body of a function component.)
If I rewrite my component.js and include the axios call directly inside useEffect instead of calling the function apiCall() from the external file, it obviously works with no problems.
I think I know it has to do with the fact that I'm using hooks in my apiCall function, and wrapping that call in a useEffect in my component.js. However, if I don't wrap it in a useEffect, it'll just run continuously and I don't want that either.
You have to follow the custom hook naming convention for this to be able to work. You can check out the documentation for that here: https://reactjs.org/docs/hooks-custom.html
Anyway, I believe in this case this should work:
import axios from 'axios';
import { useState } from 'react';
export const useApiCall = () => {
const [ resp, setResp ] = useState();
axios.get('https://some.domain/api/get').then((response) => {
setResp(response.data);
});
if(resp) return resp;
}
And then in component.js, you would call useApiCall()
Usually, we do it like this
export const useApiCall = () => {
const [ resp, setResp ] = useState();
useEffect(() => {
axios.get('https://some.domain/api/get').then((response) => {
setResp(response.data);
});
}, []);
return resp;
}
and then use it like so
export const Table = () => {
const resp = useApiCall();
return(
<>
{ resp &&
resp.map(([key, value]) => {
console.log("key: " + key);
return(
<SomeComponent />
);
})
}
</>
);
}
The prefix "use" in the function name is important, this is how we define a custom hook.
React Hook "useState" is called in function "apiCall" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
You can use following methods.
import { useState } from 'react';
export const ApiCall = () => {
const [state, setState] = useState();
};
or
import { useState } from 'react';
export const useApiCall = () => {
const [state, setState] = useState();
};
Case
I want to make isLoading (global state using React Context) value and changeIsLoading function (its changing function from IsLoadingContext.js file) becomes accessible to all files (function components and simple javascript functions).
I know that React Hooks can only be called inside of the body of a function component.
Question: So in my case here, how could I called isLoading and changeIsLoading inside a util file (non-function component or just a simple javascript function)?
What should I change from the code?
Code flow
(location: SummariesPage.js) Click the button inside SummariesPage component
(location: SummariesPage.js) Call onApplyButtonIsClicked function in SummariesPage component
(location: SummariesPage.js) Change isLoading global state into true then call fetchAPISummaries function
(location: fetchAPISummaries.js) Call fetchAPICycles function
(location: fetchAPICycles.js) Call exportJSONToExcel function
(location: exportJSONToExcel.js) Export the JSON into an Excel file then change isLoading global state into false
IsLoadingContextProvider component will be rerendered and the isLoading value in SummariesPage will be true
Error logs
Uncaught (in promise) 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
The code
IsLoadingContext.js:
import React, { useState } from 'react'
const IsLoadingContext = React.createContext()
const IsLoadingContextProvider = (props) => {
const [isLoading, setIsLoading] = useState(false)
const changeIsLoading = (inputState) => {
setIsLoading(inputState)
}
return(
<IsLoadingContext.Provider
value={{
isLoading,
changeIsLoading
}}
>
{props.children}
</IsLoadingContext.Provider>
)
}
export { IsLoadingContextProvider, IsLoadingContext }
SummariesPage.js:
import React, { useContext } from 'react'
// CONTEXTS
import { IsLoadingContext } from '../../contexts/IsLoadingContext'
// COMPONENTS
import Button from '#material-ui/core/Button';
// UTILS
import fetchAPISummaries from '../../utils/export/fetchAPISummaries'
const SummariesPage = () => {
const { isLoading, changeIsLoading } = useContext(IsLoadingContext)
const onApplyButtonIsClicked = () => {
changeIsLoading(true)
fetchAPISummaries(BEGINTIME, ENDTIME)
}
console.log('isLoading', isLoading)
return(
<Button
onClick={onApplyButtonIsClicked}
>
Apply
</Button>
)
}
export default SummariesPage
fetchAPISummaries.js:
// UTILS
import fetchAPICycles from './fetchAPICycles'
const fetchAPISummaries = (inputBeginTime, inputEndTime) => {
const COMPLETESUMMARIESURL = .....
fetch(COMPLETESUMMARIESURL, {
method: "GET"
})
.then(response => {
return response.json()
})
.then(responseJson => {
fetchAPICycles(inputBeginTime, inputEndTime, formatResponseJSON(responseJson))
})
}
const formatResponseJSON = (inputResponseJSON) => {
const output = inputResponseJSON.map(item => {
.....
return {...item}
})
return output
}
export default fetchAPISummaries
fetchAPICycles.js
// UTILS
import exportJSONToExcel from './exportJSONToExcel'
const fetchAPICycles = (inputBeginTime, inputEndTime, inputSummariesData) => {
const COMPLETDEVICETRIPSURL = .....
fetch(COMPLETDEVICETRIPSURL, {
method: "GET"
})
.then(response => {
return response.json()
})
.then(responseJson => {
exportJSONToExcel(inputSummariesData, formatResponseJSON(responseJson))
})
}
const formatResponseJSON = (inputResponseJSON) => {
const output = inputResponseJSON.map(item => {
.....
return {...item}
})
return output
}
export default fetchAPICycles
exportJSONToExcel.js
import { useContext } from 'react'
import XLSX from 'xlsx'
// CONTEXTS
import { IsLoadingContext } from '../../contexts/IsLoadingContext'
const ExportJSONToExcel = (inputSummariesData, inputCyclesData) => {
const { changeIsLoading } = useContext(IsLoadingContext)
const sheetSummariesData = inputSummariesData.map((item, index) => {
let newItem = {}
.....
return {...newItem}
})
const sheetSummaries = XLSX.utils.json_to_sheet(sheetSummariesData)
const workBook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workBook, sheetSummaries, 'Summaries')
inputCyclesData.forEach(item => {
const formattedCycles = item['cycles'].map((cycleItem, index) => {
.....
return {...newItem}
})
const sheetCycles = XLSX.utils.json_to_sheet(formattedCycles)
XLSX.utils.book_append_sheet(workBook, sheetCycles, item['deviceName'])
})
XLSX.writeFile(workBook, `......xlsx`)
changeIsLoading(false)
}
export default ExportJSONToExcel
I believe the real problem you are facing is managing the asynchronous calls. It would be much readable if you use async/await keywords.
const onApplyButtonIsClicked = async () => {
changeIsLoading(true)
await fetchAPISummaries(BEGINTIME, ENDTIME)
changeIsLoading(false)
}
You will need to rewrite fetchAPICycles to use async/await keywords instead of promises.
const fetchAPICycles = async (
inputBeginTime,
inputEndTime,
inputSummariesData
) => {
const COMPLETDEVICETRIPSURL = ...;
const response = await fetch(COMPLETDEVICETRIPSURL, {
method: "GET",
});
const responseJson = await response.json();
exportJSONToExcel(inputSummariesData, formatResponseJSON(responseJson));
};