Async Await call sending partial object - reactjs

I am new to React and following a tutorial on Youtube. I am attempting to make an API call to return a cart object, after receiving that object I would like that object to be passed to a child component where an ID property will be pulled from the cart to generate a checkout token. The issue is that when the API call is made to get the cart it will not consistently wait before passing the object to the child component through props. Sometimes it's the object, sometimes just the name of the object, sometimes an empty object. I'll write out some partial code here to demonstrate
App Component
const [cart, setCart] = useState();
const fetchCart = async() => {
setCart(await commerce.cart.retrieve();
}
useEffect(() => {
fetchCart();
},[]);
<routerstuff...>
<Route Path="/checkout" element={<Checkout cart={cart} />}>
Checkout component
const Checkout = { cart } => {
const generateToken = async () => {
commerce.checkout.generateToken(cart.id, { type = 'cart'});
useEffect(() => {
generateToken();
}, [cart]);

Related

SocketIO does not work in components of other routes in REACT

I have a component called NAV this component is static in the application, in that component I handle the socket.on listener to store the notification information, if there is a path that does not contain the socket it works normal, but when I put the socket in a component Inside of a route in react, the socket that was inside the NAV component stops working, I swear I must repeat the socket.on code of the NAV component so that the information can arrive again, this is a bad practice because the code should not be repeated so much This happens to me with 5 components in different routes, but I don't know how to prevent this from happening
I tried to make a component that covers all the components inside the routes and manages the sockets information there but it didn't work for me.
THIS IS THE CODE I HAVE IN THE SOCKET ON THE SERVER
socket.on("received event", async (id, productID) => {
if (id || productID) {
let product;
if (productID !== undefined)
product = await Product.findById(productID);
const user = getUser(id ? id : product.owner);
if (user.length > 0)
user.map((user) => io.to(user.socketId).emit("received event"));
}
});
THIS IS THE CODE THAT I HAVE IN THE NAV BUT I HAVE TO REPEAT IT IN EACH ROUTE THAT INTEGRATES THE SOCKET
useEffect(() => {
socket.on("received event", async () => {
await searchNotifications();
});
return () => socket.off();
});
THIS IS THE MAIN APP COMPONENT
function App() {
return (
<LoadingZone>
<Nav /> // HERE IS THE "SOCKET" THE ABOVE CODE THAT IS REPEATED
<Routes>
<Route path="/" element={<Home />}/>
<Route path="/report" element={<PrivateRoute><Report /></PrivateRoute>}/>
<Route path="/messages" element={<PrivateRoute><Messages /></PrivateRoute>}/>
<Route path="/notifications" element={<PrivateRoute><Notifications /></PrivateRoute>}/>
<Route path="/help/*" element={<Help />} />
<Route path="/post/information/*" element={<Post />} /> // THE SOCKET THAT IS IN THE NAV DOES NOT AFFECT IT
</Routes>
<Footer />
<PopupWindow/>
</LoadingZone>
);
}
export default App;
It's supposed to keep taking the socket from the NAV since it doesn't depend on any route but it doesn't, I have the main socket in a separate file where I can access the different components. But this is my problem
This is the nav component
import { socket } from "../api";
function Nav() {
const dispatch = useDispatch();
const searchNotifications = useCallback(async () => {
const briefNotifications = await getNotifications(cookies.get("id"));
const currentNotification = [];
let count = 0;
for (let i = 0; i < 3; i++) {
if (briefNotifications[i] !== undefined)
currentNotification.push(briefNotifications[i]);
}
for (let i = 0; i < briefNotifications.length; i++) {
if (!briefNotifications[i].view) count += 1;
}
dispatch(set(count));
dispatch(changeNotifications(currentNotification));
}, [dispatch]);
useEffect(() => {
socket.on("received event", async () => { // This works until I go to the Post route
await searchNotifications();
});
return () => socket.off();
});
Return <Content/>
}
This is the Post component
function Post() {
... // Code that has nothing to do with the problem
Return <Content/>
}
It's supposed to keep taking the socket from the NAV since it doesn't depend on any route but it doesn't
You can create global hooks to handle repeated useEffect in many components. For example like this:
export const useSocket = () => {
const [data, setData] = useState();
useEffect(() => {
socket.on("received event", async () => {
const res = await searchNotifications();
setData(res);
});
return () => socket.off();
});
return data;
}
And in any component that needs to use socket:
import { useSocket } from 'yourJsFile';
const AnyComponent = props => {
const data = useSocket();
}
Not a lot of context provided for what exactly isn't working, but it appears the useEffect hook is missing a dependency array. Without it the component(s) are constantly opening/closing (subscribing/unsubscribing) the socket connection.
Since it doesn't appear there are any external dependencies try adding an empty dependency array so the effect is run once when the component mounts to connect the socket event, and unsubscribed from the event when the component unmounts.
Example:
useEffect(() => {
socket.on("received event", async () => {
await searchNotifications();
});
return () => socket.off();
}, []); // <-- add empty dependency array

How do I pass useState data from one component file to another ReactJS

I need to share some data I have collected with useState from one component file to another. I am building a blog using Material UI that displays all posts on the home page. When a specific post is clicked I want to redirect the user to a single post page where they can read the entire thing. I have searched StackOverflow and the internet and can't seem to find a straight forward answer to the problem. I think context API might be overkill. And I am unsure how to pass props from one component to another when I am not calling the function in the other component file.
This is part of the home page component. This code stores the post id that I need to access from the SinglePost component. When I click a post it correctly console.logs the id:
export default function HomeCard() {
const [latestPosts, setLatestPost] = useState([]);
useEffect(() => {
fetch('http://localhost:8000/posts')
.then ((res) => {
return res.json();
})
.then ((data) => {
setLatestPost(data.reverse());
});
}, []);
const navigate = useNavigate();
const storePostId = (singlePostId) => {
navigate('/single-post')
console.log(singlePostId);
}
A little further down in the file:
<CardActionArea
onClick={ () =>
storePostId(latestPost.id)
}>
Within my SinglePost component:
const SinglePost = () => {
useEffect(() => {
fetch(`http://localhost:8000/posts/${singlePostId}`)
.then ((res) => {
return res.json();
})
.then ((data) => {
return (data);
});
}, []);
You can pass the prob as a query parameter.
const storePostId = (singlePostId) => {
navigate(`/single-post/${singlePostId}`)
}
and in SinglePost component read the query params using your project's router (ie: react router dom)
Edit:
You're using useNavigate from react router. I'm assuming you've had the routes set up
<Routes>
<Route path="/single-post" element={<SinglePost />} />
</Routes>
I'd recommend setting it like this
<Routes>
<Route path="/single-post/:singlePostId" element={<SinglePost />} />
</Routes>
And in your SinglePost
const SinglePost = () => {
let { singlePostId } = useParams(); //It will read it from query params
useEffect(() => {
fetch(`http://localhost:8000/posts/${singlePostId}`)
.then ((res) => {
return res.json();
})
.then ((data) => {
return (data);
});
}, [singlePostId]);

React functional components async props to children

I have a functional component (App.js) where I want to fetch some initial data using useEffect.
useEffect(() => {
const init = async () => {
const posts = await getPosts(0, 3);
const newArticles = await getArticles(posts);
setArticles(() => [...articles, ...newArticles]);
};
init();
}, []);
then I want to pass the result to a child
<ArticleList articles={articles}></ArticleList>
but in the Article component I get an empty array when I try to console.log the props.
useEffect(() => {
console.log(props.articles);
setArticles(() => props.articles);
}, [props.articles]);
How can I solve this issue?

Problems when trying to use data from an API in a React component

I am creating my first React application in a project and it is a CEP searcher (a number created by the Brazilian post office to facilitate the sending of parcels). I am using an API to get this data, I used Axios to request them from the API, I get the data, but when I pass it to a component, the component is not created and I am sure that the data is taken by the API and that the component was made correctly. I made my code like this:
const getCepData = async () => {
const { data } = await axios.get(`https://cep.awesomeapi.com.br/json/05424020`)
console.log(data)
return (
<>
<CepInfoContainer info={ data }/>
</>
);
}
You should change your logic with useState and useEffect like this
const getCepData = () => {
const [data, setData] = useState([])
useEffect(() => {
const fetchData = async () => {
const result = await axios.get(`https://cep.awesomeapi.com.br/json/05424020`);
setData(result);
}
fetchData();
},[])
return (
<>
<CepInfoContainer info={ data }/>
</>
);
}

Reload component with react hooks

I would like to ask you how to reload a component after modifying the data of a form, then I have my component:
export default function MyComponent() {
const url = "/api/1";
const [resData, setResData] = useState(null);
useEffect(() => {
const jwt = getJwt();
const fetchData = async () => {
const resP = await axios(url);
setResData(resP.data);
};
fetchData();
}, []);
return <EditComponent={resData} />
}
This component passes my data to the "EditCompoent" child component in which there is a form that is filled with data from the parent component that I can modify in which there is a save button that when I click allows me to send the modified data to my beckend:
const handleConfirm = () => {
axios.put(url, data).then((res) => {
//Reload Component
})
}
I would like to be able to reload the parent component as soon as this works is successful what could I do? I don't want to reload the whole page I just want to reload the parent component that is "MyComponent", I hope I have well posed the problem.
I'd pass the whole useEffect callback down so that handleConfirm can call it again after the axios.put, after which the resData state in the parent will be updated:
export default function MyComponent() {
const url = "/api/1";
const [resData, setResData] = useState(null);
const tryLoginJWT = () => {
const jwt = getJwt();
const resP = await axios(url);
setResData(resP.data);
};
useEffect(tryLoginJWT, []);
return <EditComponent {...{ resData, tryLoginJWT }} />
}
const handleConfirm = () => {
axios.put(url, data)
.then(tryLoginJWT)
.catch(handleErrors); // don't forget to catch here in case there's a problem
}

Resources