How can I get data from useParam hooks? - reactjs

I'm using react useParams hooks for get url params id. In component AppMessageItems i get id, but how i can send this data to getMessages function for send to endpoint. But when i try get params id in the getMessages function react give error about invalid using hooks
const AppMessageItems = ( {messages, getMessages} ) => {
let { id } = useParams();
console.log(id) // 123
useEffect( () => {
const setUserMessageDataToLocalStorage = localStorage.getItem('token');
getMessages(setUserMessageDataToLocalStorage)
}, []);
return (
<Container>
<TabPanelContainer>
<TabPanelHeader/>
<TabPanelBody />
<TabPanelFooter/>
</TabPanelContainer>
</Container>
)
};
const getMessages = (token) => async (dispatch) => {
// test data
const all = {
pass: 'Hello World',
};
try {
const getMessages = await axios.post(API.getUserMessages, {}, {headers: {"Authorization": 'Bearer ' + token}})
.then(res => res.data);
dispatch(getMessagesDispatch(all));
} catch (err) {
console.log("Error GET_MESSAGES");
}
};

As I commented. here's the walkthrough:
const AppMessageItems = ( {messages, getMessages} ) => {
let { id } = useParams();
const dispatch = useDispatch();
console.log(id) // 123
useEffect( () => {
const setUserMessageDataToLocalStorage = localStorage.getItem('token');
dispatch(getMessages(setUserMessageDataToLocalStorage, id))
}, [id, dispatch]);
return (
<Container>
<TabPanelContainer>
<TabPanelHeader/>
<TabPanelBody />
<TabPanelFooter/>
</TabPanelContainer>
</Container>
)
};
const getMessages = (token, id) => async (dispatch) => {
// use the id param whereever
// test data
const all = {
pass: 'Hello World',
};
try {
const {data} = await axios.post(API.getUserMessages, {}, {headers: {"Authorization": 'Bearer ' + token}})
dispatch(getMessagesDispatch(all));
} catch (err) {
console.log("Error GET_MESSAGES");
}
};

Related

React update state when click

import React, { useEffect, useState } from "react";
import { Container, Row } from "react-bootstrap";
import ListCategories from "./ListCategories";
import Hasil from "./Hasil";
import DaftarProduk from "./DaftarProduk";
import axios from "axios";
import keranjang from "../utils/keranjang";
import BASE_URL from "../utils/constata";
const Main = () => {
const [dataProduct, setDataProduct] = useState([]);
const [dataCategories, setDataCategories] = useState([]);
const [categoriesId, setCategoriesId] = useState(1);
const [listPesanan, setListPesanan] = useState([]);
const handleListCategories = (id) => {
setCategoriesId(id);
};
const handleProdukClick = async (produk) => {
keranjang(produk);
};
useEffect(() => {
const getProducts = async () => {
let responseJson = [];
try {
responseJson = await axios.get(
BASE_URL + "products?category.id=" + categoriesId
);
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataProduct(responseJson.data);
}
};
const getCategories = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "categories");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataCategories(responseJson.data);
}
};
const getPesanans = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "keranjangs");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setListPesanan(responseJson.data);
}
};
getProducts();
getCategories();
getPesanans();
}, [categoriesId]);
return (
<Container className="mt-3">
<Row>
{dataCategories && (
<ListCategories
categories={dataCategories}
handleClick={handleListCategories}
categoriesActive={categoriesId}
></ListCategories>
)}
{dataProduct && (
<DaftarProduk
produk={dataProduct}
handleClick={handleProdukClick}
></DaftarProduk>
)}
<Hasil pesanan={listPesanan}></Hasil>
</Row>
</Container>
);
};
export default Main;
How to update listPesanan, when handleProdukClick was click?
if me put listPesanan inside
useEffect(() => {
const getProducts = async () => {
let responseJson = [];
try {
responseJson = await axios.get(
BASE_URL + "products?category.id=" + categoriesId
);
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataProduct(responseJson.data);
}
};
const getCategories = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "categories");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataCategories(responseJson.data);
}
};
const getPesanans = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "keranjangs");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setListPesanan(responseJson.data);
}
};
getProducts();
getCategories();
getPesanans();
}, [categoriesId, listPesanan]);
that's causes looping to send request to server
my full code in here
Yes, your second 'solution' will cause looping, because you have listPesanan in you dependency array (The second parameter to the useEffect function), so whenever listPesanan changes, the useEffect is run again. And in fact, you are updating the value of listPesanan in the useEffect, which causes the useEffect to get triggered again, which causes you to update the value of listPesanan, and so on.
If I understand your question/code, a simple solution would just be to declare the getPesanans function outside of the useEffect hook, and then have your DaftarProduk onClick call that function. Then, remove listPesanan from the dependency array of useEffect. Your code would look like this:
import React, { useEffect, useState } from "react";
import { Container, Row } from "react-bootstrap";
import ListCategories from "./ListCategories";
import Hasil from "./Hasil";
import DaftarProduk from "./DaftarProduk";
import axios from "axios";
import keranjang from "../utils/keranjang";
import BASE_URL from "../utils/constata";
const Main = () => {
const [dataProduct, setDataProduct] = useState([]);
const [dataCategories, setDataCategories] = useState([]);
const [categoriesId, setCategoriesId] = useState(1);
const [listPesanan, setListPesanan] = useState([]);
**const getPesanans = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "keranjangs");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setListPesanan(responseJson.data);
}
};**
const handleListCategories = (id) => {
setCategoriesId(id);
};
const handleProdukClick = async (produk) => {
keranjang(produk);
**await getPesanans();**
};
useEffect(() => {
const getProducts = async () => {
let responseJson = [];
try {
responseJson = await axios.get(
BASE_URL + "products?category.id=" + categoriesId
);
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataProduct(responseJson.data);
}
};
const getCategories = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "categories");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataCategories(responseJson.data);
}
};
getProducts();
getCategories();
getPesanans();
}, [categoriesId]);
return (
<Container className="mt-3">
<Row>
{dataCategories && (
<ListCategories
categories={dataCategories}
handleClick={handleListCategories}
categoriesActive={categoriesId}
></ListCategories>
)}
{dataProduct && (
<DaftarProduk
produk={dataProduct}
handleClick={handleProdukClick}
></DaftarProduk>
)}
<Hasil pesanan={listPesanan}></Hasil>
</Row>
</Container>
);
};
export default Main;
problem
First handleProdukClick triggers another effect, keranjang.
const handleProdukClick = async (produk) => { // ⚠️ async misuse
keranjang(produk); // ⚠️ effect
// ✏️ update listPesanan here ...
};
code review
Your linked source code shows this implementation for keranjang. This code misunderstands how to effectively apply async and await. Let's fix that first.
⚠️ await..then anti-pattern doesn't assign result to variable
⚠️ 38-LOC function with mixed concerns
⚠️ Errors are swallowed so caller cannot respond to them
const keranjang = async (produk) => {
let pesanan = {
jumlah: 1,
total_harga: produk.harga,
product: produk,
};
const popUpPesanan = () => // ⚠️ nested function, no arguments
swal({
title: "Berhasil",
text: "Masuk Keranjang " + produk.nama,
icon: "success",
button: "Oke",
});
await axios // ⚠️ no return
.get(BASE_URL + "keranjangs?product.id=" + produk.id)
.then(async (res) => {
if (res.data.length === 0) {
await axios // ⚠️ no return
.post(BASE_URL + "keranjangs", pesanan)
.then(() => popUpPesanan())
.catch((error) => console.log(error)); // ⚠️ swallow error
} else {
pesanan = { // ⚠️ variable reassignment
jumlah: res.data[0].jumlah + 1,
total_harga: res.data[0].total_harga + produk.harga,
product: produk,
};
await axios // ⚠️ no return
.put(BASE_URL + "keranjangs/" + res.data[0].id, pesanan)
.then(() => popUpPesanan())
.catch((error) => console.log(error)); // ⚠️ swallow error
}
})
.catch((error) => console.log(error)); // ⚠️ swallow error
};
First write keranjang from a high-level.
✅ Reusable get, add, and increase functions decouple separate concerns
✅ Return Promise so handleProduckClick can know when it is complete
✅ Pass arguments to functions instead of reassigning variables
✅ Do not swallow error messages with .catch
const keranjang = async (produk) => {
const pesanan = await get(produk.id)
if (pesanan.length === 0)
return add(produk).then(() => popUpPesanan(produk))
else
return increase(pesanan[0], produk).then(() => popUpPesanan(produk))
}
✅ Implement get, add, increase
✅ Each function returns a Promise
const get = (id) =>
axios.get(`${BASE_URL}keranjangs?product.id=${id}`).then(res => res.data)
const add = (produk) =>
axios.post(`${BASE_URL}keranjangs`, {
jumlah: 1,
total_harga: produk.harga,
product: produk,
})
const increase = (pesanan, produk) =>
axios.put(`${BASE_URL}keranjangs/${pesanan.id}`, {
jumlah: pesanan.jumlah + 1,
total_harga: pesanan.total_harga + produk.harga,
product: produk,
})
popUpPesanan accepts a produk argument
const popUpPesanan = (produk) =>
swal({
title: "Berhasil",
text: "Masuk Keranjang " + produk.nama,
icon: "success",
button: "Oke",
})
fix #1
With all of that fixed, handleProdukClick can appropriately respond to the keranjang call.
const handleProdukClick = async (produk) => {
try {
await keranjang(produk) // ✅ await
setListPesanan(await getPesanans())// ✅ await and set
} catch (err) {
console.log(err) // ✅ caller handles error
}
};
You don't need async and await for this. It's easier to write
const handleProdukClick = (produk) => {
keranjang(produk).then(setListPesanan).catch(console.error)
}
fix #2
Now you have to move getPesanans out of the useEffect call in your component and decouple the mixed concerns like you did with keranjang...
import { getProducts, getCategories, getPesanans } from "./api"
const Main = () => {
const [dataProduct, setDataProduct] = useState([])
const [dataCategories, setDataCategories] = useState([])
const [categoriesId, setCategoriesId] = useState(1)
const [listPesanan, setListPesanan] = useState([])
const handleListCategories = (id) => {
setCategoriesId(id)
}
const handleProdukClick = (produk) => {
keranjang(produk).then(setListPesanan).catch(console.error)
}
useEffect(() => {
getProducts(categoriesId).then(setDataProduct).catch(console.error)
getCategories().then(setDataCategories).catch(console.error)
getPesanans().then(setListPesanan).catch(console.error)
}, [categoriesId]) // ✅ no infinite loop
return (...)
}
api
Define a reusable api module so each component does not need to concern itself with axios, building URLs, or picking apart responses.
✅ Reusable functions separate concerns
✅ .then(res => res.data) allows caller to access data directly
✅ Errors are not swallowed
✅ axios.create makes instance so you don't have to add BASE_URL to everything
import axios from "axios"
import BASE_URL from "../utils/constata";
const client = axios.create({ baseURL: BASE_URL })
const getProducts = (categoriesId) =>
client.get("/products?category.id=" + categoriesId).then(res => res.data)
const getCategories = () =>
client.get("/categories").then(res => res.data)
const getPesanans = () =>
client.get("/keranjangs").then(res => res.data)
export { getProducts, getCategories, getPesanans }
homework
✏️ Move get, add, and increase functions you wrote in keranjang to the api module.
✏️ Remove unnecessary BASE_URL
✏️ Rename them accordingly and add them to the export list
✏️ Any time you see axios spilling into your other components, refactor and move them to your api module
✏️ Consider using transformRequest and transformResponse in your axios config so you don't have to add .then(res => res.data) to each request
✏️ Consider decoupling swal and keranjang. keranjang can move to the api module and swal can be called from your component.
// api.js
const keranjang = async (produk) => {
const pesanan = await get(produk.id)
if (pesanan.length === 0)
return add(produk)
else
return increase(pesanan[0], produk)
}
// component.js
const Main = () => {
// ...
const handleProdukClick = (produk) => {
keranjang(produk)
.then(setListPesanan)
.then(_ => swal({
title: "Berhasil",
text: "Masuk Keranjang " + produk.nama,
icon: "success",
button: "Oke",
}))
.catch(console.error)
}
// ...
}
✏️ util/keranjang is now an empty module and can be removed

ReactJS - passing data between components

I want to passing data between components but I have a problem. Not getting any error as long as I don't passing data yet, it's fine. When I try to show the props in the console, I can easily see what I want (history,match,location,AuthStore). But when I try to pass the data, I can only see value and AuthStore in the console and value returns empty. Where am I wrong?
front.layout.js
import Profile from '../../Views/profile'
const Layout = (props) => {
const [user, setUser] = useState({});
const [isLoggedIn, setIsLoggedIn] = useState(false);
props.AuthStore.getToken();
const history = useHistory();
useEffect(() => {
const token =
props.AuthStore.appState != null
? props.AuthStore.appState.user.access_token
: null;
axios
.post(
"/api/authenticate",
{},
{
headers: {
Authorization: "Bearer " + token,
},
}
)
.then((res) => {
if (!res.data.isLoggedIn) {
history.push("/login");
}
setUser(res.data.user);
setIsLoggedIn(res.data.isLoggedIn);
})
.catch((error) => {
history.push("/login");
});
}, []);
return (
<>
<Profile value={user} />
</>
)
index.js
const Profile = (props) => {
console.log(props);
const { params } = props.match;
const [data, setData] = useState({});
const history = useHistory();
if(props.location.key){
useEffect(() => {
axios
.get(
`/api/${params.username}`,
{
headers: {
Authorization:
"Bearer " +
props.AuthStore.appState.user.access_token,
},
}
)
.then((res) => {
if (res.data.username) {
setData(res.data);
}
})
.catch((error) => {
console.log(error);
});
}, []);
}

why is recipe.map not a function?

I am trying to pass data into a component, however I am getting an error it is saying recipe.map is not a component. Would love some help.
const App = () => {
const [recipe, setRecipe] = useState([]);
const appId = `af783d30`;
const appKey = ``;
const url = `https://api.edamam.com/search?q=chicken&app_id=${appId}&app_key=${appKey}&from=0&to=3&calories=591-722&health=alcohol-free`;
console.log(url);
useEffect(() => {
const fetchData = async () => {
try {
const res = await axios.get(url);
setRecipe(res.data);
console.log(res.data);
} catch (err) {
console.error(err);
}
};
fetchData();
}, []);
return (
<div>
{recipe.map((r) => {
return <RecipeCard recipe={r} />;
})}
</div>
);
};
export default App;

Wait for useLazyQuery response

I need to call a query when submit button is pressed and then handle the response.
I need something like this:
const [checkEmail] = useLazyQuery(CHECK_EMAIL)
const handleSubmit = async () => {
const res = await checkEmail({ variables: { email: values.email }})
console.log(res) // handle response
}
Try #1:
const [checkEmail, { data }] = useLazyQuery(CHECK_EMAIL)
const handleSubmit = async () => {
const res = await checkEmail({ variables: { email: values.email }})
console.log(data) // undefined the first time
}
Thanks in advance!
This works for me:
const { refetch } = useQuery(CHECK_EMAIL, {
skip: !values.email
})
const handleSubmit = async () => {
const res = await refetch({ variables: { email: values.email }})
console.log(res)
}
After all, this is my solution.
export function useLazyQuery<TData = any, TVariables = OperationVariables>(query: DocumentNode) {
const client = useApolloClient()
return React.useCallback(
(variables: TVariables) =>
client.query<TData, TVariables>({
query: query,
variables: variables,
}),
[client]
)
}
You could also use the onCompleted option of the useLazyQuery hook like this:
const [checkEmail] = useLazyQuery(CHECK_EMAIL, {
onCompleted: (data) => {
console.log(data);
}
});
const handleSubmit = () => {
checkEmail({ variables: { email: values.email }});
}
In case someone wants to fetch multiple apis at single load, it could be achieved like this.
On Demand Load > e.g. onClick, onChange
On Startup > e.g. useEffect
import { useLazyQuery } from "#apollo/client";
import { useState, useEffect } from "react";
import { GET_DOGS } from "../../utils/apiUtils";
const DisplayDogsLazy = () => {
const [getDogs] = useLazyQuery(GET_DOGS);
const [data, setData] = useState([]);
useEffect(() => {
getAllData();
}, []);
const getAllData = async () => {
const response = await getDogs();
console.log("Awaited response >", response);
};
const handleGetDogsClick = async () => {
const response = await getDogs();
setData(response.data.dogs);
};
return (
<>
<button onClick={handleGetDogsClick}>Get Dogs</button>
{data?.length > 0 && (
<ul>
{data?.map((dog) => (
<li key={dog.id} value={dog.breed}>
{dog.breed}
</li>
))}
</ul>
)}
</>
);
};
export default DisplayDogsLazy;

React useEffect hook infinity loop

I keep encountering an infinity loop when trying to use the useEffect hook to fetch and set data. ive tried 3 variations of the hook and they all produce a loop, how do i stop this from happening?
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
})
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},[])
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},[profile.posts])
EDIT: Here is the PostApi.getPostsByUser code
getPostsByUser: (userId, token) => {
return(
axios
.get("/api/posts/by/" + userId, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
})
.then(response => {
console.log("Posts by User");
console.log(response.data);
return response.data;
})
.catch(err => console.log(err))
)
}
EDIT: Function component code:
const Posts = () => {
const [{auth}] = useAuth();
const [{profile},, setPosts] = useProfile()
useEffect(() => {
PostApi.getPostsByUser(auth.user._id, auth.token)
.then(response => setPosts(response));
},[]);
console.log(profile)
return(
<div className="User-Post">
<div className="New-Post">
<NewPost />
</div>
<div className="User-Posts-Content">
{
profile.posts ? profile.posts.map((item, key) => {
return <Post post={item} key={key} />
}) : null
}
</div>
</div>
)
}
export default Posts
Change:
const [auth] = useAuth();
const [profile, setPosts] = useState();
const setPosts = posts => { setPosts(state => ({ ...state, profile: {
...state.profile, posts: posts } })) }
getPostsByUser: (userId, token) => {
return(
axios
.get("/api/posts/by/" + userId, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
});
}
and
useEffect(() => {
PostApi.getPostsByUser(auth.user._id, auth.token)
.then(response => setPosts(response.data));
},[]);
You can try like this.
useEffect(() => {
const get = async () => {
const response = await PostApi.getPostsByUser(auth.user._id, auth.token);
setPosts(response);
}
get();
},[]);
This works for me ... and the simplest solution too
const [toggle, setToggle] = useState(false);
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},toggle)

Resources