Trouble problem with onChange event in react - reactjs

I tired to put checked and unchecked on the input checkbox.
Indeed i have a list of checkbox items, i get them form API, Then i would like to crossed or leave unchecked them and send to data base. I crossed some of them and i can successfully get the crossed checkboxes from database. Now i try to uncheck them and i post them again. In the next time, when i reload the page i see there are still crossed checkboxes.
here you can see the code.
Can any one to solve my puzzle?
Her you can see the part of my code.
Thank you.
import React, { useContext, useEffect } from "react";
import Form from "react-bootstrap/Form";
import Csc from "country-state-city";
import { useHistory, useLocation } from "react-router-dom";
import salone from "../utils/salone";
function UserProfile(props) {
const [interests, setInterests] = React.useState("");
const [selectedInterests] = React.useState([]);
useEffect(() => {
salone.get(`/user/info`, {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
})
.catch((error) => {
console.log(error.response.data.detail);
history.push("/login");
})
.then((response) => {
let intIds = response?.data?.interests; // intIds it is Array like intIds =[{"id": 1, "name": "drink"}, ...] or empty
salone.get(`/auth/signup/interests`)
.then((response) => {
let interestItems = [];
for (let inter of response.data) { //here: response.data=[{"id": 1, "name": "drink"}, ...]
interestItems.push(
<div className="col-6" key={inter.id}>
<div className="row">
<div className="col-2">
<input
key={inter.id}
id={inter.id}
name="newsletter"
type="checkbox"
onChange={(e) =>{ console.log(e, e.target)
return interestChange(e.target)}}
/>
</div>
<div className="col-10">
<p className="p-14">{inter.name}</p>
</div>
</div>
</div>
);
}
setInterests(interestItems);
for (let elem of intIds) {
document.getElementById(elem.id).setAttribute("checked", "checked");
selectedInterests.push(elem.id);
}
})
.catch((err) => {
console.log(err);
});
});
}, []);
const interestChange = (elem) => {
console.log(elem, elem.checked, elem.id);
console.log(selectedInterests);
if (elem.checked) {
selectedInterests.push(parseInt(elem.id));
console.log(selectedInterests);
}
else {
let newInterests = selectedInterests;
newInterests.splice(newInterests.indexOf(parseInt(elem.id)), 1);
console.log(newInterests);
}
}
const handleSubmit = (event) => {
event.preventDefault();
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.stopPropagation();
} else {
let url = `/user/update?interest_id_list=${selectedInterests.join(",")}`;
salone.post(url,
null,
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}
)
.then((response) => {
if (response != undefined) {
history.push({
pathname: "/message",
state: {
message: "Aggiornamento effettuato con successo!",
type: "confirm",
back: true,
//link: "/login",
//label: "Go back to Login page",
img: imgNotification}
});
}
})
.catch((err) => {
}
});
}
};
return (
<div>
<div className="row w-100 m-0">
<Form method="post" onSubmit={handleSubmit}>
<div className="row mt-2">
{interests}
</div>
</Form>
</div>
</div>
);
}
export default UserProfile;

Here i try to found the solution for my puzzle
import React, { useContext, useEffect } from "react";
import Form from "react-bootstrap/Form";
import Csc from "country-state-city";
import { useHistory, useLocation } from "react-router-dom";
import salone from "../utils/salone";
function UserProfile(props) {
const [interests, setInterests] = React.useState({});
const [selectedInterests, setSelectedInterests] = React.useState([]);
useEffect(() => {
salone.get(`/user/info`, {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
})
.catch((error) => {
console.log(error.response.data.detail);
history.push("/login");
})
.then((response) => {
if(Array.isArray(response?.data?.interests)){
setSelectedInterests(response?.data?.interests.map((interest) => interest.id));
}
salone.get(`/auth/signup/interests`)
.then((response) => {
let interestItems = {};
for (let inter of response.data) {
interestItems[inter.id]= inter;
}
setInterests(interestItems);
})
.catch((err) => {
console.log(err);
});
});
}, []);
const handleSubmit = (event) => {
event.preventDefault();
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.stopPropagation();
} else {
let url = `/user/update?interest_id_list=${selectedInterests.join(",")}`;
salone.post(url,
null,
{
headers: {
"content-type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}
)
.then((response) => {
if (response != undefined) {
history.push({
pathname: "/message",
state: {
message: "Aggiornamento effettuato con successo!",
type: "confirm",
back: true,
//link: "/login",
//label: "Go back to Login page",
img: imgNotification}
});
}
})
.catch((err) => {
}
});
}
};
return (
<div>
<div className="row w-100 m-0">
<Form method="post" onSubmit={handleSubmit}>
<div className="row mt-2">{Object.keys(interests).map((interstId) => {
interstId = +interstId;
let interest = interests[interstId];
//console.log(selectedInterests, interstId,
selectedInterests.indexOf(interstId));
return <div className="col-6" key={interest.id}>
<div className="row">
<div className="col-2">
<input
key={interest.id}
id={interest.id}
name="newsletter"
type="checkbox"
checked = {selectedInterests.indexOf(interstId) > -1 ? "checked" : ""}
onChange={(e) =>{
let index = selectedInterests.indexOf(interstId);
if(index > -1){
selectedInterests.splice(index, 1);
setSelectedInterests([...selectedInterests])
}else{
setSelectedInterests([...selectedInterests, interstId])
}
}}
/>
</div>
<div className="col-10">
<p className="p-14">{interest.name}</p>
</div>
</div>
</div>
})}</div>
</Form>
</div>
</div>
);
}
export default UserProfile;

Related

How to get data when language changed in react V18.2?

I have problem with fetching data when language changed. I tried a lot of things that I found from Stack overflow, but unfortunately it just changing the direction and it didn't fetch the data based on language changed.
I fetching data with a custom hook and call it inside my functional component. let me share the code that I write.
Note: I'm using I18nextLng for translation.
App.js
import { RouterProvider } from "react-router-dom";
import Loading from './components/loading';
import routes from './routes/routes';
import { useEffect } from "react";
import i18n from "./utils/i18n";
function App() {
useEffect(() => {
let cleanup = true;
if (cleanup) {
i18n.on('languageChanged', (local) => {
let direction = i18n.dir(local);
document.body.dir = direction;
})
}
return () => {
cleanup = false;
};
}, []);
return (
<RouterProvider router={routes} fallbackElement={<Loading />} />
)
}
export default App;
LanguageSwitcher.js
import { useTranslation } from "react-i18next";
const LanguageSwitcher = () => {
const { i18n } = useTranslation();
return (
<select
className="form-select-sm rounded-pill text-center"
aria-label="Default select example"
value={i18n.language}
onChange={(e) =>
i18n.changeLanguage( e.target.value)
}
>
<option value="en">English</option>
<option value="fa">دری</option>
</select>
);
}
export default LanguageSwitcher;
Internships.js
import Image from "react-bootstrap/Image"
import { useFetchWebsiteData } from "../../hooks/website/useFetchWebsiteData";
import Loading from '../../components/loading'
import { useEffect, useState } from "react";
const Internships = () => {
let lang = localStorage.getItem("i18nextLng")
const { data, isLoading } = useFetchWebsiteData("getInternship", lang);
console.log("language changed", language);
return !isLoading ? (
<div className="container-fluid news-wrapper">
<div className="container">
<div className="row py-5">
<div className="col-md-12">
<div className="col-md-8">
<h4 className="title mb-4">{data?.title}</h4>
<p className="sub-title">{data?.content}</p>
</div>
<div className="col-md-2 text-center">
<Image
src={require("../../images/internships.png")}
fluid={true}
/>
</div>
</div>
</div>
</div>
</div>
) : (
<Loading />
);
}
export default Internships;
useFetchWebsiteData.js (Custom hook for fetching data)
import { useState, useEffect } from "react";
import { axiosPublic } from "../../utils/axios";
export const useFetchWebsiteData = (url,lang) => {
const [data, setData] = useState({});
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
// const lang = localStorage.getItem("i18nextLng");
console.log('lang inside hook', lang)
useEffect(() => {
// const controller = new AbortController()
const fetchData = async () => {
setIsLoading(true);
await axiosPublic
.get(url, {
headers: { lang: lang === "fa" ? "dr" : "en" },
// signal: controller.signal,
})
.then((response) => {
if (response.status === 200) {
if (lang === "en") {
setIsLoading(false);
response.data.data.en.map((d) => {
let title = d.title;
let content = d.content;
return setData({ title: title, content: content });
});
}
if (lang === "fa") {
setIsLoading(false);
console.log("fa intern", response.data.data.dr)
response.data.data.dr.map((d) => {
let title = d.title;
let content = d.content;
return setData({ title: title, content: content });
});
setIsLoading(false);
}
} else {
setIsError(true);
}
})
.catch((error) => {
setIsLoading(false);
setIsError(true);
console.error(error.message);
});
};
fetchData();
// return () => {
// controller.abort()
// };
}, [url, lang]);
return { data, isLoading, isError };
};
I really appreciate for your helping.

Tree menu by consuming API and Params

I have a question and at the same time ask you for help because I have a problem in my coding.
So, I intend to display the "City" menu based on the "Province" parameter in my menu component tree.
I previously managed to display the provincial name menu in the tree menu component, the result is like this.
Well, later when the menu names of the provinces will appear the names of the cities based on the "province" parameter. However, when I tried it, it failed. The result is like this
Here's my code =
Building.js
export const getBuildingOLDallProvinsi = () => {
return new Promise((resolve, reject) => {
axios
.get(`${baseUrl}/api/nad/buildingCount`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
if (error.response?.data.code === 404)
resolve({ lists: [], totalCount: 0 });
console.log(error.response);
reject(error?.response?.data?.message || "Network error.");
});
});
};
export const getBuildingOLDallKota = (provinsi) => {
return new Promise((resolve, reject) => {
axios
.get(`${baseUrl}/api/nad/buildingCount/${provinsi}`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
if (error.response?.data.code === 404)
resolve({ lists: [], totalCount: 0 });
console.log(error.response);
reject(error?.response?.data?.message || "Network error.");
});
});
};
TreeData.jsx
import React, { useEffect, useState } from "react";
import TreeMenu from "react-simple-tree-menu";
import "react-simple-tree-menu/dist/main.css";
import {
getBuildingOLDallProvinsi,
getBuildingOLDallKota,
} from "../../../service/building";
import { useParams } from "react-router-dom";
const TreeData = () => {
const [countData, setCount] = useState([]);
const [countData2, setCount2] = useState([]);
const getDataAllProvinsi = () => {
getBuildingOLDallProvinsi()
.then((resolve) => {
console.log(resolve);
setCount(resolve);
})
.catch((reject) => {
console.log(reject);
});
};
const { provinsi } = useParams();
const getDataAllKota = (param) => {
getBuildingOLDallKota({ ...param, provinsi: provinsi })
.then((resolve) => {
console.log(resolve);
setCount2(resolve);
})
.catch((reject) => {
console.log(reject);
});
};
useEffect(() => {
getDataAllProvinsi();
getDataAllKota();
}, []);
return (
<>
<div className="row">
<div className="col text-center">
<p className="mt-3">
<div className="row mt-3 d-flex justify-content-center cursor-pointer">
<div className="col-lg-8 text-left text-dark">
<TreeMenu
cacheSearch
data={[
{
key: "provinsi",
label: "Provinsi",
nodes: countData.map((data) => {
return {
key: data.id,
label: [data.provinsi, data.total_building],
nodes: [
{
key: "kota",
label: "Kota",
nodes: countData2.map((data) => {
return {
key: data.provinsi,
label: [data.kota, data.total_building],
nodes: [
{
key: data.id,
label: data.total_building,
nodes: [],
},
],
};
}),
},
],
};
}),
},
]}
debounceTime={125}
disableKeyboard={false}
hasSearch={false}
onClickItem={function noRefCheck() {}}
resetOpenNodesOnDataUpdate={false}
/>
</div>
</div>
</p>
</div>
</div>
</>
);
};
export default TreeData;
Thank you in advance, your help is very helpful for me and I really respect all of your answers.
Edit = this is my response API
pastebin(dot)com/Bua3FThZ
pastebin(dot)com/ERSCHDSR
Update:
In the console I can display param data based on the province.
For example, the data for the province is "ACEH", then the ACEH data appears in the console ...
const getDataAllKota = (param) => {
getBuildingOLDallKota("ACEH")
.then((resolve) => {
console.log(resolve);
setCount2(resolve);
})
.catch((reject) => {
console.log(reject);
});
};
console.log(countData2);
Now I'm confused about how to display param data by province in the component tree menu.

Cast to ObjectId failed for value \"updateInstock\" (type string) at path \"_id\" for model \"Product\

I'm trying to adjust my countInStock based off of qty ordered. In my PlaceOrderScreen.js, I have items = item.map(product => ({ _id: product._id, countInStock: product.countInStock, qty: product.qty, new:(product.countInStock - product.qty) })) where item = cart.carItems. I'm trying to get this to my backend in my productRouter.js, so that I can update my products based off of their new countInStock, but I'm getting "Cast to ObjectId failed for value \"updateInstock\" (type string) at path \"_id\" for model \"Product\". I'm unsure of why this is happening, and I would really appreciate any help or advice. Thank you!
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {createOrder} from '../actions/orderActions';
import {updateInstock} from '../actions/productActions';
import { ORDER_CREATE_RESET } from '../constants/orderConstants';
export default function PlaceOrderScreen(props) {
const cart = useSelector((state) => state.cart);
if (!cart.paymentMethod) {
props.history.push('/payment');
}
const orderCreate = useSelector((state) => state.orderCreate);
const { loading, success, error, order } = orderCreate;
const item = cart.cartItems
const items = item.map(product => ({ _id: product._id, countInStock: product.countInStock, qty: product.qty, new:(product.countInStock - product.qty) }))
const dispatch = useDispatch();
const placeOrderHandler = () => {
dispatch(createOrder({ ...cart, orderItems: cart.cartItems }));
dispatch(updateInstock(items));
};
useEffect(() => {
if (success) {
props.history.push(`/order/${order._id}`);
dispatch({ type: ORDER_CREATE_RESET });
}
}, [dispatch, order, props.history, success]);
return (
<div>
<div className="row top">
<div className="col-1">
<div className="card card-body">
<ul>
<li>
<div className="row">
...
</div>
</li>
<li>
<button
type="button"
onClick={placeOrderHandler}
className="primary block"
disabled={cart.cartItems.length === 0}
>
Place Order
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
);
}
productActions.js
import Axios from 'axios';
import {
PRODUCT_UPDATEINSTOCK_REQUEST,
PRODUCT_UPDATEINSTOCK_SUCCESS,
PRODUCT_UPDATEINSTOCK_FAIL,
} from '../constants/productConstants';
import { BASE_URL } from '../constants/app.constants';
export const updateInstock = (items) => async (dispatch, getState) => {
dispatch({ type: PRODUCT_UPDATEINSTOCK_REQUEST, payload: items });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.put(`${BASE_URL}/api/products/updateInstock`, items, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: PRODUCT_UPDATEINSTOCK_SUCCESS, payload: data });
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message;
dispatch({ type: PRODUCT_UPDATEINSTOCK_FAIL, error: message });
}
};
productRouter.js
productRouter.put(
'/updateInstock',
expressAsyncHandler(async (req, res) => {
console.log(req.body.items)
})
);
console.log(req.body.items) is not showing data in the terminal.

How to handle error and success response the cleanest way?

How would I show a server response error or success message the cleanest way ?
Right now, I'm using an async function to make an axios request, and on success/error im just updating a local state (with react-hook-form), but I feel like it's "ugly" and I want the pages to be as clean as possible and put the code to handle success and error messages in the service request, behind the scenes.
Example :
ForgotPassword.jsx
import React, { useState } from 'react';
import Layout from '../components/core/Layout';
import axios from 'axios';
import { useForm } from 'react-hook-form';
import { Button, Form, Alert } from 'react-bootstrap';
import { regex } from '../constants';
import { isAuth } from '../helpers';
import { forgotPassword } from '../services/User';
import { Redirect } from 'react-router-dom';
const Forgot = () => {
const {
handleSubmit,
register,
errors,
getValues,
setError,
setValue,
clearError
} = useForm({
mode: 'onBlur'
});
register({ name: 'responseError' });
register({ name: 'responseSuccess' });
const { responseSuccess } = getValues();
const onSubmit = async values => {
try {
const response = await forgotPassword(values);
setValue([{ responseSuccess: response.data.message }]);
// set response success msg to local state responseSuccess
} catch (error) {
setError('responseError', '', error);
// set response error msg to local state responseError
}
};
const forgotPasswordForm = () => (
<>
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group>
<Form.Label>Email address</Form.Label>
<Form.Control
name='email'
ref={register({
required: true,
pattern: {
value: regex.email,
message: 'Invalid email address'
}
})}
type='email'
placeholder='Enter email'
isInvalid={errors.email}
/>
<Form.Control.Feedback type={errors.email ? 'invalid' : 'valid'}>
{errors.email && errors.email.message}
</Form.Control.Feedback>
</Form.Group>
<Button variant='primary' type='submit'>
Submit
</Button>
</Form>
<br />
{errors.responseError && (
<Alert
variant='danger'
dismissible
onClose={() => clearError('responseError')}>
{errors.responseError.message}
</Alert>
)}
</>
);
const forgotPasswordFormSuccess = () => (
<Alert
variant='success'
className='mt-5'
dismissible
onClose={() => setValue([{ responseSuccess: '' }])}>
{responseSuccess}
</Alert>
);
if (isAuth()) return <Redirect to='/' />;
return (
<Layout>
<div>
<h1>Forgot password</h1>
{responseSuccess ? forgotPasswordFormSuccess() : forgotPasswordForm()}
</div>
</Layout>
);
};
export default Forgot;
forgotPassword Function
export const forgotPassword = async ({ email }) => {
return new Promise(async (resolve, reject) => {
try {
const response = await Axios({
method: 'PUT',
url: `${process.env.REACT_APP_API}/forgot-password`,
data: { email }
});
resolve(response);
} catch (error) {
if (error.response) {
reject(error.response && error.response.data.error);
}
reject('Something went wrong. please try again later.');
}
});
};
Hope this is what you want
export const forgotPassword = ({ email }) => {
return new Promise((resolve, reject) => {
axios(`${process.env.REACT_APP_API}/forgot-password`, {
method: 'PUT',
data: { email }
})
.then(res => resolve(res.data))
.catch(err => reject(err))
});
};
const onSubmit = values => {
forgotPassword(values)
.then(res => setValue([{ responseSuccess: res.message }]))
.catch(err => setError('responseError', '', err));
};

Fetch Requests in different pages with React Hooks

I'm building a website using React Hooks and I've got two different pages (Workshops.js and Shows.js) fetching data from the same API, just with different parameters (?type=0 and ?type=1).
Once the data is fetched I'm mapping the results (It would be nice to have a reusable component there..see the comments in the code below). When the user click either on a show or a workshop he will be redirected to the same page.
Now singularly the code works.
Is there a more elegant way to avoid repeating the same code? ...something like Services in Angular?
Thank you!
Here is Workshop.js.
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'
import api from '../../maps/Api'
const Workshops = () => {
const [ workshops, setWorkshop ] = useState([])
const [ isLoading, setIsLoading ] = useState(false)
const [ error, setError ] = useState(null)
const GET_URL = api.get.workshops /* http://someapi/workshops?type=0 */
useEffect(() => {
setIsLoading(true)
fetch(GET_URL, {headers: {
"Accept": "application/json",
"Access-Control-Allow-Origin": "*"
}})
.then(res => {
return (res.ok) ? res.json() : new Error("Mistake!")
})
.then(workshops => {
if(workshops.upcoming) {
setWorkshop(workshops.upcoming);
}
setIsLoading(false);
})
.catch(error => {
setError(error)
})
}, [GET_URL])
if ( error ){ return <p>{ error.message }</p> }
if ( isLoading ){
return <p>Loading workshops...</p>
}
return(
<main>
<div className='content'>
<div className='contentCol'>
<ul id='workshopBox'>
{
workshops.map( (workshop, i) => (
<li> // FROM HERE...
<div
className='workshop-active'>
<h2>{ workshop.title }</h2>
<p>{ workshop.description }</p>
<p>{ workshop.place }</p>
<p>{ (new Date(workshop.date).toLocaleDateString("it-IT", {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}))}</p>
<p>{ (new Date(workshop.date).toLocaleTimeString("it-IT", {
hour: '2-digit',
minute: '2-digit',
hour12: true
}))}</p>
<p> Full price { workshop.price_full + ', 00'} € </p>
<p> Early bird price { workshop.price_earlybirds + ', 00'} € </p>
<p>
<Link to={`/workshops/${ workshop.id}`}>
<button>Details</button>
</Link>
</p>
<br/>
</div>
</li> //..to HERE I WOULD LIKE TO USE A REUSABLE COMPONENT
))
}
</ul>
</div>
</div>
</main>
)
}
export default Workshops
and here's Shows.js
import React, { useState, useEffect } from 'react';
//import { Link } from 'react-router-dom'
import api from '../maps/Api'
const Spettacoli = () => {
const [ shows, setShows ] = useState([])
const [ isLoading, setIsLoading ] = useState(false)
const [ error, setError ] = useState(null)
const GET_URL = api.get.shows /* http://someapi/workshops?type=1 */
useEffect(() => {
setIsLoading(true)
fetch(GET_URL, {headers: {
"Accept": "application/json",
"Access-Control-Allow-Origin": "*"
}})
.then(res => {
return (res.ok) ? res.json() : new Error("Mistake!")
})
.then(shows => {
setShows(shows)
setIsLoading(false)
})
.catch(error => {
setError(error)
})
}, [GET_URL])
return(
<main>
<div className='content'>
<div className='contentCol'>
/* SAME INTERFACE AS WORKSHOP */
</div>
</div>
</main>
)
}
export default Shows
So you may create your custom hook:
function useMyDataFetch(GET_URL) {
const [ data, setData ] = useState([])
const [ isLoading, setIsLoading ] = useState(true)
const [ error, setError ] = useState(null)
useEffect(() => {
let hasBeenAborted = false; // added
setIsLoading(true)
fetch(GET_URL, {headers: {
"Accept": "application/json",
"Access-Control-Allow-Origin": "*"
}})
.then(res => {
return (res.ok) ? res.json() : new Error("Mistake!")
})
.then(data => {
if (hasBeenAborted) return; // added
if(data.upcoming) {
setData(data.upcoming);
}
setIsLoading(false);
})
.catch(error => {
if (hasBeenAborted) return; // added
setIsLoading(false); // added
setError(error)
});
return () => { hasBeenAborted = true; } // added
}, [GET_URL]);
return { data, error, isLoading };
}
and use that in your components.
Notice lines I've marked with // added.
hasBeenAborted allows us react in case GET_URL has been updated for any reason for the same component. Cleanup in useEffect is really important so we avoid race conditions.
Instead of hasBeenAborted flag we could use AbortController but with that we would still fall into catch branch and need additional if to distinguish if request has been cancelled or actually failed. So just matter of taste to me.
As for your components they will use hook like that:
const Workshops = () => {
const {isLoading, error, data: workshops} = useMyDataFetch(api.get.workshops);
if ( error ){ return <p>{ error.message }</p> }
if ( isLoading ){
return <p>Loading workshops...</p>
}
return(
// the same here
);
}
export default Workshops

Resources