How to fetch data instantly after post request - useEffect react - javascript

I want to know if that is the correct way i do it.
Firstly i fetch the hard coded data from my API and display it into the screen. I also have a form from which i send data and i want the axios.get method to instantly fetch the newest updated data that was sent from a form. I made a helper state that is put in to "useEffect array dependencies" and whenever that state changes its value, app reloads and fetches again.
Context file with useEffect hook:
import { createContext, useState, useEffect } from "react";
import { apiService } from "../services/api/api.service";
const Context = createContext({
footer: false,
subjectForm: false,
storedSubjects: [],
footerVisibilityHandler: () => {},
subjectFormVisibilityHandler: () => {},
});
export const ContextProvider = ({ children }) => {
const [footer, setFooter] = useState(false);
const [subjectForm, setSubjectForm] = useState(false);
const [storedSubjects, setStoredSubjects] = useState([]);
const [send, setSend] = useState(false);
useEffect(() => {
const getData = async () => {
try {
const getSubjects = await apiService.getSubjects();
const tableRow = getSubjects.data.map((subject) => {
return {
name: subject.name,
};
});
setStoredSubjects(tableRow);
} catch (err) {
console.log(err);
}
};
getData();
}, [send]);
const footerVisibilityHandler = () => {
setFooter((previousState) => !previousState);
setSubjectForm(false);
};
const subjectFormVisibilityHandler = () => {
setSubjectForm((previousState) => !previousState);
};
const context = {
footer,
subjectForm,
storedSubjects,
footerVisibilityHandler,
subjectFormVisibilityHandler,
setSend,
};
return <Context.Provider value={context}>{children}</Context.Provider>;
};
export default Context;
Form from which i send data:
import Context from "../../store/context";
import FormContainer from "../UI/FormContainer";
import { apiService } from "../../services/api/api.service";
import { useContext, useRef } from "react";
const AddSubject = () => {
const ctx = useContext(Context);
const subject = useRef("");
const sendData = (e) => {
e.preventDefault();
ctx.setSend((prevState) => !prevState);
apiService.addSubject(subject.current.value);
};
return (
<FormContainer show={ctx.subjectForm} send={sendData}>
<label htmlFor="subject">Subject Name: </label>
<input type="text" id="subject" ref={subject} />
<button type="submit">Add</button>
</FormContainer>
);
};
export default AddSubject;
Api endpoints file:
import axios from "axios";
const api = () => {
const baseUrl = "https://localhost:5001/api";
let optionAxios = {
headers: {
"Content-Type": "multipart/form-data",
},
};
return {
getSubjects: () => axios.get(`${baseUrl}/subjectcontroller/getsubjects`),
addSubject: (subjectName) =>
axios.post(
`${baseUrl}/subjectcontroller/createsubject`,
{
name: subjectName,
},
optionAxios
),
};
};
export const apiService = api();

Related

Fetching data with axios in a custom react hook doesn't return data

I used this code to show the id of a product.
import { useState } from 'react';
import axios from 'axios';
const BASE_URL = "https://makeup-api.herokuapp.com/api/v1/products";
const useGetProduct = () => {
const [products, setProducts] = useState([]);
const [singleProduct, setSingleProduct] = useState(null);
const getTopProducts = () => {
axios.get(`${BASE_URL}.json`, {
params: {
product_tags: 'Canadian',
},
})
.then(Response => setProducts(Response.data));
};
const getSingleProduct = () => {
axios.get(`${BASE_URL}/1048.jason`)
.then(Response => setSingleProduct(Response.data));
};
return {
products,
getTopProducts,
singleProduct,
getSingleProduct,
}
};
export default useGetProduct;
import React, { useEffect } from "react";
import { useParams } from "react-router-dom";
import useGetProduct from "../hooks/useGetProduct";
const Product = () => {
const { id } = useParams();
const { singleProduct, getSingleProduct } = useGetProduct();
useEffect(() => {
getSingleProduct();
}, []);
return (
<div>
<p>Product: {id}</p>
</div>
);
};
export default Product;
app.js
<Route exact path="/product/:id" element={<Product />} />
But when I change <p>Product: {id}</p> to <p>Product: {singleProduct?.name}</p> the product name does not display. Instead, it just shows Product: without the details about the product on localhost and I am not sure why.
const Product = () => {
const { id } = useParams();
const { singleProduct, getSingleProduct } = useGetProduct();
useEffect(() => {
getSingleProduct();
}, []);
return (
<div>
<p>Product: {singleProduct?.name}</p>
</div>
);
};
export default Product;
I want to show details about a single product via a hook in react js.
const { id } = useParams();
const { singleProduct, getSingleProduct } = useGetProduct();
useEffect(() => {
getSingleProduct();
}, []);
You are not using the id from useParams. Shouldn't you pass it to getSingleProduct, I assume that singleProduct is undefined.

How can I make the data inside of my Cart become persistent?

I have a NextJS application that is using the ShopifyBuy SDK. I have been successfully able to implement a solution where I am able to fetch the products from Store and display them to the User. The user is also able to go to a product page and add the product to the cart.
However, when the user refreshes the page, the cart is reset, and the data does not persist. The code is below:
context/cart.js:
import { createContext, useContext, useEffect, useReducer } from "react";
import client from "../lib/client";
import Cookies from "js-cookie";
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = "SET_CART";
const initalState = {
lineItems: [],
totalPrice: 0,
webUrl: "",
id: "",
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const savedState = Cookies.get("cartState");
const [state, dispatch] = useReducer(reducer, savedState || initalState);
useEffect(() => {
Cookies.set("cartState", state, { expires: 7 });
}, [state]);
useEffect(() => {
getCart();
}, []);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
const getCart = async () => {
try {
const cart = await client.checkout.create();
setCart(cart);
} catch (err) {
console.log(err);
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={{ state }}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);
products/[handle].tsx:
import React, { useState, useEffect } from "react";
import client from "../../lib/client";
import { useCartDispatch, useCartState } from "../../context/cart";
import Link from "next/link";
import cookie from "js-cookie";
export const getStaticPaths = async () => {
const res = await client.product.fetchAll();
const paths = res.map((product: any) => {
return {
params: { handle: product.handle.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context: any) => {
const handle = context.params.handle;
const res = await client.product.fetchByHandle(handle);
const product = JSON.stringify(res);
return {
props: {
product,
},
};
};
function Product({ product }: any) {
const { state } = useCartState();
const { setCart } = useCartDispatch();
const addToCart = async () => {
const checkoutId = state.id;
const lineItemsToAdd = [
{
variantId: product.variants[0].id,
quantity: 1,
},
];
const res = await client.checkout.addLineItems(checkoutId, lineItemsToAdd);
setCart(res);
};
product = JSON.parse(product);
return (
<div>
<div className=" flex-col text-2xl font-bold m-8 flex items-center justify-center ">
<h1>{product.title}</h1>
<button onClick={addToCart}>Add to Cart</button>
<Link href="/cart">Checkout</Link>
</div>
</div>
);
}
export default Product;
pages/cart/index.tsx:
import React, { useEffect } from "react";
import { useCartState, useCartDispatch } from "../../context/cart";
import client from "../../lib/client";
function Cart() {
const { state } = useCartState();
return (
<div>
<h1>Cart</h1>
{state.lineItems &&
state.lineItems.map((item: any) => {
return (
<div key={item.id}>
<h2>{item.title}</h2>
<p>{item.variant.title}</p>
<p>{item.quantity}</p>
</div>
);
})}
</div>
);
}
export default Cart;
I have tried using a library called js-cookie and also localStorage. I'm not sure where the problem lies or if the solutions that I've tried are wrong.
P.S.: I'm fairly new to NextJS and Typescript so go easy on the syntax. This code is for a personal project. Thanks in advance!
Answering this because I ended up coming up with a solution that works for me, at least.
Here it is:
const getCart = async () => {
try {
const checkoutId = Cookies.get("checkoutId");
let cart;
if (checkoutId) {
cart = await client.checkout.fetch(checkoutId);
} else {
cart = await client.checkout.create();
Cookies.set("checkoutId", cart.id);
}
setCart(cart);
} catch (err) {
console.log(err);
}
};
From my understanding, what this does is the following:
Check the cookies to see if one exists called "checkoutId"
If it exists, fetch the cart using that checkoutId
Otherwise, create a new cart and create a cookie using the cart.id that is returned in the response
Then, inside my individual Product page ([handle].tsx), I'm doing the following:
const addToCart = async () => {
const checkoutId = state.id;
const lineItemsToAdd = [
{
variantId: product.variants[0].id,
quantity: 1,
},
];
const res = await client.checkout.addLineItems(checkoutId, lineItemsToAdd);
console.log(res);
if (cookie.get("checkoutId") === undefined) {
cookie.set("checkoutId", res.id);
}
setCart(res);
};
Using cookies to store your object cart, as far as I know, is not a good idea. You could use localStorage, like so:
import { createContext, useContext, useEffect, useReducer } from "react";
import client from "../lib/client";
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = "SET_CART";
const initalState =
typeof localStorage !== "undefined" && localStorage.getItem("cartState")
? JSON.parse(localStorage.getItem("cartState"))
: {
lineItems: [],
totalPrice: 0,
webUrl: "",
id: "",
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initalState);
useEffect(() => {
localStorage.set("cartState", JSON.stringify(state));
}, [state]);
useEffect(() => {
getCart();
}, []);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
const getCart = async () => {
try {
const cart = await client.checkout.create();
setCart(cart);
} catch (err) {
console.log(err);
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={{ state }}>{children}</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);

How to send array of items to localstorage within context api? (REACT)

I'm developing a pokedex using pokeAPI through react, but I'm developing a feature where I can favorite pokemons and with that through a context, I can store the names of these pokemons in a global array. I've already managed to test and verify that the pokemon names are going to this "database" array inside the pokeDatabase const in my context, but my goal now is to pass this array to localstorage so that the browser recognizes these favorite pokemons instead of disappearing every time I refresh the page, my solution was to try to create a useEffect inside the context so that every time I refresh my application, this information is saved in localStorage, but without success. What better way to achieve this?
context:
import { createContext } from "react";
const CatchContext = createContext({
pokemons: null,
});
export default CatchContext;
provider
import React, { useEffect } from "react";
import CatchContext from "./Context";
const pokeDatabase = {
database: [],
};
const CatchProvider = ({ children }) => {
useEffect(() => {
const dataStorage = async () => {
await localStorage.setItem('pokemons', JSON.stringify(pokeDatabase.database));
}
dataStorage();
}, [])
return (
<CatchContext.Provider value={{ pokemons: pokeDatabase }}>
{children}
</CatchContext.Provider>
);
}
export default CatchProvider;
pageAddPokemon
import * as C from './styles';
import { useContext, useEffect, useState } from 'react';
import { useApi } from '../../hooks/useApi';
import { useNavigate, useParams } from 'react-router-dom';
import PokeInfo from '../../components/PokeInfo';
import AddCircleOutlineIcon from '#mui/icons-material/AddCircleOutline';
import DoNotDisturbOnIcon from '#mui/icons-material/DoNotDisturbOn';
import CatchContext from '../../context/Context';
const SinglePokemon = () => {
const api = useApi();
const { pokemons } = useContext(CatchContext);
const { name } = useParams();
const navigate = useNavigate();
const handleHompage = () => {
navigate('/');
}
const [loading, setLoading] = useState(false);
const [imgDatabase, setImgDatabase] = useState('');
const [infoPokemon, setInfoPokemon] = useState([]);
const [pokemonTypes, setPokemonTypes] = useState([]);
const [isCatch, setIsCatch] = useState(false);
useEffect(() => {
const singlePokemon = async () => {
const pokemon = await api.getPokemon(name);
setLoading(true);
setImgDatabase(pokemon.sprites);
setInfoPokemon(pokemon);
setPokemonTypes(pokemon.types);
setLoading(false);
console.log(pokemons.database);
}
singlePokemon();
verifyPokemonInDatabase();
}, []);
const verifyPokemonInDatabase = () => {
if (pokemons.database[infoPokemon.name]) {
return setIsCatch(true);
} else {
return setIsCatch(false);
}
}
const handleCatchAdd = async () => {
if (isCatch === false) {
if (!pokemons.database[infoPokemon.name]);
pokemons.database.push(infoPokemon.name);
setIsCatch(true);
}
}
const handleCatchRemove = async () => {
if (isCatch === true) {
if (!pokemons.database[infoPokemon.name]);
pokemons.database.splice(pokemons.database.indexOf(toString(infoPokemon.name)), 1);
setIsCatch(false);
}
}
return (
<C.Container>
<PokeInfo
name={infoPokemon.name}
/>
<C.Card>
<C.Info>
<C.Imgs>
<img src={imgDatabase.front_default} alt="" />
<img src={imgDatabase.back_default} alt="" />
</C.Imgs>
<h2 id='types'>Tipos</h2>
{pokemonTypes.map(type => {
return (
<C.Types>
<h2>{type.type.name}</h2>
</C.Types>
)
})}
{isCatch ? (
<DoNotDisturbOnIcon id='iconCatched' onClick={handleCatchRemove}/>
): <AddCircleOutlineIcon id='icon' onClick={handleCatchAdd}/>}
</C.Info>
</C.Card>
<C.Return>
<button onClick={handleHompage}>Retornar a Pokédex</button>
</C.Return>
</C.Container>
)
}
export default SinglePokemon;

Array doesn't save information ReactJS

import './App.css';
import io from 'socket.io-client'
import { useEffect, useRef, useState } from 'react'
import React from 'react';
import ReactDOM from "react-dom/client";
const socket = io.connect("http://localhost:3001");
function App() {
const [message, setMessage] = useState("");
const [state, setState] = useState([]);
const [chat, setChat] = useState([]);
const socketRef = useRef();
const sendMessage = () => {
socket.emit("send_message", { message });
};
const renderChat = () => {
return (
chat.map(msg => {
console.log(msg.data)
return (
<h3>{msg.data["message"]}</h3>
)
})
)
}
useEffect(() => {
socketRef.current = io.connect("http://localhost:3001")
socketRef.current.on("receive_message", ({ message }) => {
setChat([ ...chat, { message } ])
})
return () => socketRef.current.disconnect()
},
[ chat ]
)
return (
<div className="App">
<input placeholder="Message..." onChange={(event) => {
setMessage(event.target.value);}}
/>
<button onClick={sendMessage}>Send Message</button>
<h1>Message:</h1>
{renderChat()}
</div>
);
}
export default App;
For some reason the useEffect that needs to store information doesn't work. I have tried a few solutions to store new values in an array useState but I always get this error:
When I do it like this:
useEffect(() => {
socket.on("receive_message", message => {
setChat([...chat, {message}]);
});
}, [socket])
it works but it doesn't save the information (it always has only 1 value which is the latest input text).
You can do it like in the second approach you mentioned, using the previous State:
useEffect(() => {
socket.on("receive_message", message => {
setChat(prevState => [...prevState, {message}]);
});
}, [socket])
You try
useEffect(() => {
socket.on("receive_message", ({ message }) => {
if(!!message){
setChat(prev => [ ...prev, { message } ])
}
})
return () => socket.disconnect()
},[ socket ])

React 'useContext' hook not re-rendering after context updates with database data

I am using React's Context API to share data that most of my components need.
The Context is initially defined, but shortly receives data from the Firebase database (please see IdeaContext.tsx). I define the context in a functional component and the display component, which returns a small card based on the information received.
However, the component doesn't render when I start the development server with Yarn. Instead, in order to get it to render, I have to write console.log('something') inside the display component and then it suddenly re-renders. However, when I refresh the server, it again doesn't render.
How can I make my component render immediately (or at least after the context updates with the data from the database?)
Code:
Context Definition:
import React, { createContext, useEffect, useState } from "react";
import { IdeaContextType, Idea } from "../t";
import {ideasRef} from './firebase'
function getIdeas() {
var arr: Array<Idea> = [];
ideasRef.on('value', (snapshot) => {
let items = snapshot.val()
snapshot.forEach( (idea) => {
const obj = idea.val()
arr.push({
title: obj.title,
description: obj.description,
keyID: obj.keyID
})
console.log(arr)
})
})
return arr
}
const IdeaContextDefaultValues: IdeaContextType = {
ideas: [],
setIdeas: () => {},
};
const IdeaContext = createContext<IdeaContextType>(IdeaContextDefaultValues)
const IdeaContextProvider: React.FC = ({ children }) => {
const [ideas, setIdeas] = useState<Array<Idea>>(
IdeaContextDefaultValues.ideas);
useEffect( ()=> {
console.log('getting info')
setIdeas(getIdeas())
}, [])
useEffect( () => {
console.log('idea change: ', ideas)
}, [ideas])
return (
<IdeaContext.Provider value={{ ideas, setIdeas }}>
{children}
</IdeaContext.Provider>
);
};
Displayer and Card Component
import React, { FC, ReactElement, useContext } from "react";
import IdeaCreator from "./IdeaCreator";
import { IdeaContext } from "./IdeaContext";
import { Idea } from "../t";
import { Link } from "react-router-dom";
const IdeaPost:React.FC<Idea> = ({title, keyID, description}):ReactElement => {
console.log('Received',title,description,keyID)
return (
<div className="max-w-sm rounded overflow-hidden shadow-lg">
<img
className="w-full"
src="#"
alt="Oopsy daisy"
/>
<div className="px-6 py-4">
<div className="font-bold text-xl mb-2"> <Link to={"ideas/" + keyID} key= {keyID}> {title}</Link> </div>
<p className="text-gray-700 text-base">{description}</p>
</div>
</div>
);
};
const IdeaDisplay:FC<any> = (props:any):ReactElement => {
const { ideas, setIdeas } = useContext(IdeaContext)
console.log('Ideas in display: ', ideas)
console.log('test') //This is what I comment and uncommend to get it to show
return (
<div className="flex flex-wrap ">
{ideas.map((idea) => {
console.log(idea)
console.log('Sending',idea.title,idea.description,idea.keyID)
console.log(typeof idea.keyID)
return (
<IdeaPost
title={idea.title}
description={idea.description}
keyID = {idea.keyID}
key = {idea.keyID * 100}
/>
);
})}
</div>
);
};
export default IdeaDisplay;
Solution Code:
import React, { createContext, useEffect, useState } from "react";
import { IdeaContextType, Idea } from "../t";
import {ideasRef} from './firebase'
async function getIdeas() {
var arr: Array<Idea> = [];
const snapshot = await ideasRef.once("value");
snapshot.forEach((idea) => {
const obj = idea.val();
arr.push({
title: obj.title,
description: obj.description,
keyID: obj.keyID,
});
console.log(arr);
});
return arr
}
const IdeaContextDefaultValues: IdeaContextType = {
ideas: [],
setIdeas: () => {},
};
const IdeaContext = createContext<IdeaContextType>(IdeaContextDefaultValues)
const IdeaContextProvider: React.FC = ({ children }) => {
const [ideas, setIdeas] = useState<Array<Idea>>(
IdeaContextDefaultValues.ideas);
useEffect(() => {
console.log("getting info");
const setup = async () => {
const ideas = await getIdeas();
setIdeas(ideas);
};
setup()
}, []);
useEffect( () => {
console.log('idea change: ', ideas)
const updateDatabase = async () => {
await ideasRef.update(ideas)
console.log('updated database')
}
updateDatabase()
}, [ideas])
return (
<IdeaContext.Provider value={{ ideas, setIdeas }}>
{children}
</IdeaContext.Provider>
);
};
export {IdeaContext, IdeaContextProvider}
First of all you would need to use once and not on if you want to get the data only once. If you want to use a realtime listener you could send the setIdeas to your function. Also try to be carefull with async/away calls to the Firebase sdk. Your code could look like this:
import React, { createContext, useEffect, useState } from "react";
import { IdeaContextType, Idea } from "../t";
import { ideasRef } from "./firebase";
async function getIdeas() {
var arr: Array<Idea> = [];
const snapshot = await ideasRef.once("value");
let items = snapshot.val();
snapshot.forEach((idea) => {
const obj = idea.val();
arr.push({
title: obj.title,
description: obj.description,
keyID: obj.keyID,
});
console.log(arr);
});
return arr;
}
const IdeaContextDefaultValues: IdeaContextType = {
ideas: [],
setIdeas: () => {},
};
const IdeaContext = createContext < IdeaContextType > IdeaContextDefaultValues;
const IdeaContextProvider: React.FC = ({ children }) => {
const [ideas, setIdeas] =
useState < Array < Idea >> IdeaContextDefaultValues.ideas;
useEffect(() => {
console.log("getting info");
const getData = async () => {
const ideas = await getIdeas();
setIdeas(ideas);
};
}, []);
useEffect(() => {
console.log("idea change: ", ideas);
}, [ideas]);
return (
<IdeaContext.Provider value={{ ideas, setIdeas }}>
{children}
</IdeaContext.Provider>
);
};

Categories