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
Related
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 have a problem with the rendering in the react, when I subscribe to a socket.io event, this data coming once at 300ms (it's an array of objects). Each time the data comes from the socket, my component is rendered, whether that data is the same or not.
Here is my socket.js file:
import socketClient from "socket.io-client";
const ENDPOINT = `http://localhost:8080/?token=admin`;
export const socket = socketClient(ENDPOINT);
export const subscribeToData = (cb) => {
socket.on(`realtime-data`, data => cb(data));
}
Now, here is my App.js file :
import React, { useEffect, useState } from "react";
import {
subscribeToData,
} from "./utils/socketIO";
import ChildComponent from "./Components/ChildComponent/ChildComponent";
function App() {
const [data, setData] = useState([]);
let objectData = {};
useEffect(() => {
subscribeToData((socketData) => {
setData((prevState) => {
if (prevState !== socketData)
return socketData
return prevState
});
});
}, []);
return (
<>
{data.length > 0 && (
<ChildComponent
data={data}
/>
)}
</>
);
}
export default App;
How can I fix the problem so that when I receive new data, my child component will be rendered, but when I receive the same data, it will not be rendered?
Thank you guys, for your time.
EDIT!!!!
I make this things and work,I don't know if is the perfect solution but it work:
export const subscribeToData = (cb) => {
let prevStateData = []
socket.on(`realtime-data`, data => {
if (JSON.stringify(prevStateData) !==
JSON.stringify(data)) {
prevStateData = data
cb(data)
}
});
}
You can transform the array of objects into a string using JSON.stringify() to compare the old and new state:
useEffect(() => {
subscribeToData(socketData => {
let oldSocket = JSON.stringify(data)
let newSocket = JSON.stringify(socketData)
if(oldSocket !== newSocket)
setData(socketData)
})
}, [])
If you compare strings instead of array of objects, you can prevent React from rerendering your component.
I use React for fetching voting objects of a GraphQL API, provided by AWS Amplify. Therefore I created following function that works with async/await:
import { useState, useEffect } from 'react';
import { API } from 'aws-amplify';
import { getVote } from 'src/graphql/queries';
const asGetVoting = (id) => {
const [vote, setVote] = useState([]);
const fetchVoting = async () => {
try {
const voteData = await API.graphql({
query: getVote, variables: { id }
});
setVote(voteData.data.getVote);
} catch (error) {
console.log('Fetching error: ', error);
}
};
useEffect(() => {
fetchVoting();
}, []);
return vote;
};
export default asGetVoting;
In my component I call the function above and I want to wait until the whole object is fetched - without success:
import asGetVoting from 'src/mixins/asGetVoting';
const Voting = () => {
const fetchVoting = asGetVoting(id);
fetchVoting.then((voting) => {
console.log('Voting completely loaded and ready to do other stuff');
}).catch((error) => {
console.log(error);
});
return (
<div>
some code
</div>
);
};
export default Voting;
Any idea what I am doing wrong? Respectively how can I wait until the object is loaded for querying its content? Or is my fetching function (asGetVoting) built in a wrong way? Do I mix async/await stuff with promises?
Thank you for your appreciated feedback in advance.
I think this is a little more complex than it needs to be. If API is returning a promise, you could set your state using .then to ensure the promise has resolved (I didn't included it but should probably add a catch statement as well). Something like:
const asGetVoting = (id) => {
const [vote, setVote] = useState([]);
useEffect(() => {
API.graphql({
query: getVote, variables: { id }
}).then(result => setVote(result.data.getVote))
}, []);
return (
// Whatever logic you are using to render vote state
<div>{vote}</div>
)
};
I'm trying to make a page to show the details of each video.
I fetched multiple video data from the back-end and stored them as global state.
This code works if I go to the page through the link inside the app. But If I reload or open the URL directory from the browser, It can not load the single video data.
How should I do to make this work?
Thanx
Single Video Page
import { useState, useEffect, useContext } from "react";
import { useParams } from "react-router-dom";
import { VideoContext } from "../context/videoContext";
const SingleVideo = () => {
let { slug } = useParams();
const [videos, setVideos] = useContext(VideoContext);
const [video, setVideo] = useState([]);
useEffect(() => {
const result = videos.find((videos) => {
return videos.uuid === slug;
});
setVideo((video) => result);
}, []);
return (
<>
<div>
<h1>{video.title}</h1>
<p>{video.content}</p>
<img src={video.thumbnail} alt="" />
</div>
</>
);
};
export default SingleVideo;
Context
import React, { useState, createContext, useEffect } from "react";
import Axios from "axios";
import { AxiosResponse } from "axios";
export const VideoContext = createContext();
export const VideoProvider = (props) => {
const [videos, setVideos] = useState([]);
const config = {
headers: { "Access-Control-Allow-Origin": "*" },
};
useEffect(() => {
//Fetch Vidoes
Axios.get(`http://localhost:5000/videos`, config)
.then((res: AxiosResponse) => {
setVideos(res.data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<VideoContext.Provider value={[videos, setVideos]}>
{props.children}
</VideoContext.Provider>
);
};
I think the reason is because when you refresh the app, you fetch the video data on context and the useEffect on your single video page component runs before you receive those data.
To fix you can simply modify slightly your useEffect in your single video component to update whenever you receive those data:
useEffect(() => {
if (videos.length) {
const result = videos.find((videos) => {
return videos.uuid === slug;
});
setVideo((video) => result);
}
}, [videos]);
I have been trying to use a cleanup function to cancel the API call I a user presses the back button before the request is resolved.
However I still receive the same error "Warning: Can't perform a React state update on an unmounted component.".
I am using fetch function, I added the abortController but still I receive the same warning.
import React, { useState, useEffect, useReducer, useContext } from "react";
import { ActivityIndicator } from "react-native";
import AllThumbnails from "../components/AllThumbnails";
import reducer from "../functions/reducer";
import { lightColors, darkColors } from "../constants/Colors";
import { ThemeContext } from "../context/context";
import ScreenContainer from "../components/UI/ScreenContainer";
export default function AllCatScreen(props) {
const { navigation, route } = props;
const [categories, setCategories] = useState([]);
const [state, dispatch] = useReducer(reducer, { catPage: 1 });
const [theme] = useContext(ThemeContext);
const { taxonomy } = route.params;
useEffect(() => {
const abortCtrl = new AbortController();
const opts = { signal: abortCtrl.signal };
let isActive = true;
fetch(`${siteURL}/wp-json/wp/v2/${taxonomy.endPoint}`, opts)
.then((response) => response.json())
.then((res) => {
if (isActive) {
setCategories([...categories, ...res]);
}
})
.catch((err) => console.log(err));
return function cleanup() {
isActive = false;
console.log(isActive);
abortCtrl.abort();
};
}, []);
if (categories.length == 0) {
return (
<ScreenContainer notYet={true}>
<ActivityIndicator size="large" color={theme.colors.text} />
</ScreenContainer>
);
} else {
return (
<ScreenContainer notYet={false}>
<AllThumbnails
data={categories}
navigation={navigation}
catThumb={true}
action={[state, dispatch]}
fetchData={fetchData}
/>
</ScreenContainer>
);
}
}
I have read that react native should support the AbortController. I am using Expo SDK 38 but even in the clean up function logging the console doesn't work. Does anyone know what's wrong?