How to use local storage in React js - reactjs

This is my code:
function EditCourseTable() {
const [data, setData] = useState([]);
const [CourseID, setCourseID] = useState(0);
useEffect(() => {
Axios
.get("http://localhost:3003/coursestable")
.then(result => setData(result.data));
}, []);
return (
<div className="main">
<h2>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Course Number</th>
<th scope="col">Course Name</th>
<th scope="col">View Details</th>
<th scope="col">Edit Course</th>
<th scope="col">Delete Course</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
{localStorage.setItem('CourseID', item.CourseID)}
<td>{item.CourseID}</td>
<td>{item.Name}</td>
<td>View</td>
<td><a href={`/editcourse2`} className="btn btn-primary" >
Edit</a></td>
<td><button className="btn btn-primary">Delete</button></td>
</tr>
})}
</tbody>
</table>
</h2>
</div>
)
}
export default EditCourseTable;
I use the localStorage to store the CourseId that the user click on (when click in Edit or View), but it is store the last courseID in the table, not the courseID that I click on. Whats the error?

You should insert the value in the localStorage by triggering a function called on click of an element
function storeCourse(id) {
localStorage.setItem('CourseID', id)
}
<td>
<span
className="btn btn-primary"
onClick={() => storeCourse(item.CourseID)}>
View
</span>
</td>

You need to create something to catch that click, so you can create some function like
const handleClickItem = (courseID) => {
localStorage.setItem('CourseID', courseID)
}
So whenever the user click, it will use onClick, so you can pass something like onClick = { () => handleClickItem(item.CourseID)} then pass the item.CourseID into that handleClickItem
Now the handleClickItem has the courseID
That's when you localStorage.setItem('CourseID', item.CourseID)
function EditCourseTable() {
const [data, setData] = useState([]);
const [CourseID, setCourseID] = useState(0);
useEffect(() => {
Axios
.get("http://localhost:3003/coursestable")
.then(result => setData(result.data));
}, []);
//- Add handleClickItem
const handleClickItem = (courseID) => {
localStorage.setItem('CourseID', courseID)
}
return
Inside the return, the map one, just add onClick where ever you want the user to click
for example:
<tr key={id} onClick = {() => handleClickItem(item.CourseID)}>

Your localStorage code runs when rendered so the last rendered item's id is saved to localStorage. You should use the function onClick.
<tbody>
{data.map((item, id) => {
return <tr key={id} onClick={() => localStorage.setItem('CourseID', item.CourseID)}>
<td>{item.CourseID}</td>
<td>{item.Name}</td>
<td>View</td>
<td><a href={`/editcourse2`} className="btn btn-primary" >
Edit</a></td>
<td><button className="btn btn-primary">Delete</button></td>
</tr>
})}
</tbody>

In your code you save data to local storage on items render. All items saves to local storage on key CourseID in render order.
Because of this after items render local storage CourseID value equal last item in rendered collection.
Right chose for solving this problem is saving data to local storage on link click.
But i think you does not need saving this data to local storage. React allow storing this in state.
Example for your code:
const [clickedCourseId, setClickedCourseId] = useState(null);
...
render (
...
{data.map((item, id) => {
return (
<tr key={id}>
<td>{item.CourseID}</td>
<td>{item.Name}</td>
<td>View</td>
<td><a href={`/editcourse2`} onClick={() => { setClickedCourseId(item.CourseID) }} className="btn btn-primary" >
Edit</a></td>
<td><button className="btn btn-primary">Delete</button></td>
</tr>
)
})}
In this example, when you click on View or Edit links, clickedCourseId being filled clicked item CourseId and you does not need to store it in localStorage.
However, if you want to store it in localStorage, you can change setClickedCourseId to your localStorage setItem

Related

How send id category in link React?

I have page where I display all categories with this code
{categories.map((category, index) => {
return(
<tr key={index}>
<th scope="row">{index}</th>
<td>{category.name}</td>
<td>50</td>
<td><Link to={{pathname: `/categories/edit/`}}>Edit</Link></td>
<td><Button variant={'danger'} onClick={deleteCategoryHandler} data-id={category._id}>Delete</Button></td>
</tr>
)
})}
But when I create Component edit Category, and there I Need send with id. which will be better ID and name?
you need to do this:
categories.map((category, index) => {
const location = {
pathname: `/categories/edit/`,
state: {
category,
},
};
return (
<tr key={index}>
<th scope="row">{index}</th>
<td>{category.name}</td>
<td>50</td>
<td>
<Link
to={location}
>
Edit
</Link>
</td>
<td>
<Button
variant={"danger"}
onClick={deleteCategoryHandler}
data-id={category._id}
>
Delete
</Button>
</td>
</tr>
);
});
and in the destination component just use something like this to get location object and then its state property:
const location = useLocation();
const category = location?.state?.category;
If you need to send a category id, then an incredibly easy way is to send it as part of the path URL.
Create a route in your Router that handles path="/categories/edit/:id". Then construct the appropriate path for the link when mapping.
{categories.map((category, index) => {
return(
<tr key={index}>
<th scope="row">{index}</th>
<td>{category.name}</td>
<td>50</td>
<td>
<Link to={{pathname: `/categories/edit/${category.id}`}}>
Edit
</Link>
</td>
<td>
<Button
variant={'danger'}
onClick={deleteCategoryHandler}
data-id={category._id}
>
Delete
</Button>
</td>
</tr>
)
})}
On the receiving route's component the id parameter can be accessed via match object using the useParams react hook from react-router-dom.
const { id } = useParams();
Add category id variable in your link.
{categories.map((category, index) => {
return(
<tr key={index}>
<th scope="row">{index}</th>
<td>{category.name}</td>
<td>50</td>
<td><Link to={{pathname: `/categories/edit/ID/${category._id}`}}>Edit</Link></td>
<td><Button variant={'danger'} onClick={deleteCategoryHandler} data-id={category._id}>Delete</Button></td>
</tr>
)
})}
In your hook Component use useParam() to get category id
import {useParams} from "react-router-dom";
const YourComponent = (props) => {
let { ID } = useParams();
...
}

React: Updating multiple object variables in array

I want to implement a voting system in my app where you can vote thumbs-up or thumbs-down for a selected movie. The ratings will be saved in a ratings array of objects that contain movie title, id, thumbs-up votes and thumbs-down votes. If a movie has no votes, the first vote will add the new object into the ratings array, and subsequent votes will update the thumbs-up votes and thumbs-down votes of the object. My current code works for the first thumbs-up vote and adds the new obj into the array, but does not update the vote count for subsequent votes. How can I update the votes in the movie object for both thumbs-up and thumbs-down votes? This involves incrementing the count depending on whether the thumbs-up or thumbs-down button is clicked. How can I handle this besides creating separate but similar functions for each button like I’ve already started to do? Any help is greatly appreciated, thank you in advance!
const ThumbRating = ( {id, title} ) => {
const [thumbsUpCount, setthumbsUpCount] = useState(0);
const [thumbsDownCount, setthumbsDownCount] = useState(0);
const [ratings, setRatings] = useState([]);
const newUpVote = (id) => {
if (thumbsUpCount === 0 && thumbsDownCount === 0) {
setthumbsUpCount(thumbsUpCount + 1);
const obj = {
id: {id},
title: {title},
thumbsUpCount: thumbsUpCount +1,
thumbsDownCount: thumbsDownCount
}
setRatings([obj])
} else {
setthumbsUpCount(thumbsUpCount +1)
handleThumbsUp(id, thumbsUpCount +1)
}
}
const handleThumbsUp = (id, thumbsUpCount) => {
setRatings(ratings.map(obj => {
if (obj.id !== id) return obj
return {...obj, thumbsUpCount: thumbsUpCount +1}
}))
}
return (
<div className="thumb-rating">
<p>Would you recommend this movie?</p>
<table>
<tbody>
<tr>
<td>
<div >
<button className="thumbs-up" onClick={() => newUpVote(id)}>
<i className="fa fa-thumbs-up fa-4x" />
</button>
</div>
</td>
<td>
<div >
<button className="thumbs-down" onClick={() => setthumbsDownCount(thumbsDownCount + 1)}>
<i className="fa fa-thumbs-down fa-4x" />
</button>
</div>
</td>
</tr>
<tr>
<td>
<h2>Yes: {thumbsUpCount} </h2>
</td>
<td>
<h2>No: {thumbsDownCount} </h2>
</td>
</tr>
</tbody>
</table>
</div>
)
}
There is My solution:
The handlerHandleCountDown is working and the state is correcting updating.
Now you can try to handle the countDown based on the example.
Do not forget to pass id and title as props to your component. 🙂
import { useState } from 'react'
const ThumbRating = ({ id, title }) => {
const [thumbsUpCount, setthumbsUpCount] = useState(0)
const [thumbsDownCount, setthumbsDownCount] = useState(0)
const [ratings, setRatings] = useState([])
const newUpVote = (id) => {
if (thumbsUpCount === 0 && thumbsDownCount === 0) {
setthumbsUpCount(thumbsUpCount + 1)
const obj = {
id, // id:id also works but id: {id} will not
title,
thumbsUpCount: thumbsUpCount + 1,
thumbsDownCount: thumbsDownCount,
}
setRatings([obj])
} else {
setthumbsUpCount(thumbsUpCount + 1)
handleThumbsUp(id)
}
}
const handleThumbsUp = (id) => {
// Is better to use the find method when you want to find a single element in an array
const objectToBeUpdated = ratings.find((obj) => obj.id === id)
objectToBeUpdated.thumbsUpCount += 1 // I assumed you will always update by one
setRatings([objectToBeUpdated])
}
return (
<div className='thumb-rating'>
<p>Would you recommend this movie?</p>
<table>
<tbody>
<tr>
<td>
<div>
<button className='thumbs-up' onClick={() => newUpVote(id)}>
<i className='fa fa-thumbs-up fa-4x' />
</button>
</div>
</td>
<td>
<div>
<button
className='thumbs-down'
onClick={() => setthumbsDownCount(thumbsDownCount + 1)}
>
<i className='fa fa-thumbs-down fa-4x' />
</button>
</div>
</td>
</tr>
<tr>
<td>
<h2>Yes: {thumbsUpCount} </h2>
</td>
<td>
<h2>No: {thumbsDownCount} </h2>
</td>
</tr>
</tbody>
</table>
</div>
)
}
export default ThumbRating

Get a value from a <i> tag with hooks in react

I'm trying to get a value from i tag with hook. When I click on it but in console, the result is undefined.
Here is my code snippet.
const deleteCar = event => {
const car_id = event.target.value
console.log(car_id)
}
<tbody>
{
props.cars && props.cars.map( car =>
(
<tr key={car.id}>
<td>{car.model}</td>
<td><i className="far fa-trash-alt" name="car_id" value={car.id} onClick={deleteCar}></i><i className="far fa-trash-alt"></i></td>
</tr>
)
)}
</tbody>
How can I do that?
Thanks!
I think the issue is that you are not passing the event in your function onClick. It should be: onClick={(event) =>deleteCar(event) }
You can do this in two ways. But both will need a state to hold the car.id for you. (**Reminder: since you are using <i> tag, you can't just put value as prop. That's for <input> tag only)
Using React Hook (useEffect) - better option (proven to work every time):-
const [carId, setCarId] = useState('')
const [isDelete, setIsDelete] = useState(false)
// handle delete of car
const deleteCar = () => {
if(isDelete) {
// perform delete
console.log(carId)
// reset states involve
setCardId('')
setIsDelete(false)
}
}
// invoke delete function
useEffect(()= > {
deleteCar()
}, [isDelete])
<tbody>
{props.cars && props.cars.map( car => (
<tr key={car.id}>
<td>{car.model}</td>
<td>
<i
className="far fa-trash-alt"
onClick={() => {
setCarId(car.id)
setIsDelete(true)
}}
>
</i>
<i className="far fa-trash-alt"></i>
</td>
</tr>
))}
</tbody>
Just use normal onClick function:-
const [carId, setCarId] = useState('')
// handle delete of car
const deleteCar = () => {
// perform delete
console.log(carId)
// reset states involve
setCardId('')
}
<tbody>
{props.cars && props.cars.map( car => (
<tr key={car.id}>
<td>{car.model}</td>
<td>
<i
className="far fa-trash-alt"
onClick={() => {
setCarId(car.id)
deleteCar()
}}
>
</i>
<i className="far fa-trash-alt"></i>
</td>
</tr>
))}
</tbody>
if it was an input tag for example you could access its value attribute through event.target.value, but that not the case for icon tag. Use the getAttribute method in order to access it:
const deleteCar = event => {
const car_id = event.target.getAttribute('value')
console.log(car_id)
}

How can I pass props to another components with in reactjs

I'm trying to pass product data from AllProducts component to Product component.
AllProducts.jsx: is showing all the products I have and Product.jsx will show specific product and how can I pass data to Product.jsx?
Here is my AllProducts.jsx:
const AllProducts = (props) => {
const [products, setProducts] = useState([]);
const getProductsAPI = () => {
axios
.get("http://localhost:8000/api/products")
.then((res) => {
setProducts(res.data);
getProductsAPI();
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
getProductsAPI();
}, [props]);
return (
<div>
<table className="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{products.map((product, i) => (
<tr key={i}>
<th scope="row">{i}</th>
<td>{product.title}</td>
<td>
<Link to={`/products/${product._id}`}> View </Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
and here is my Product.jsx:
const Product = (props) => {
return (
<div className="container">
<h4>{props.product.title}</h4>
</div>
);
};
export default Product;
Here is my project github if you want to look at all the code I have: https://github.com/nathannewyen/full-mern/tree/master/product-manager
If the data is fully loaded for each product in AllProducts, and you don't want to make another API call by product id in the Product component, in this case, you don't have to use a route link to view Product, just make a conditional rendering to show Product component inside AllProducts component. pseudo-code as below,
const [showProduct, setShowProduct] = useState(false);
const [currentProduct, setCurrentProduct] = useState();
const showProduct = (product) => {
setShowProduct(true);
setCurrentProduct(product);
}
<tbody>
{products.map((product, i) => (
<tr key={i}>
<th scope="row">{i}</th>
<td>{product.title}</td>
<td>
<button type="button" onclick = {showProduct(product)}>View</button>
</td>
</tr>
))}
</tbody>
return (showProduct ? <Product /> : <AllProucts/>)
If you also need to make another API call to get extra data for each product, then use the router link but perhaps you can not pass props.

REACT: Instead of sorting by clicking a button - sorting by clicking header "id" in table

I make request to server and I get response. Response it data which I display in view table-list. Also now I try implement when I click button changeAsc happen sort by asc-desc.
But I need that sort by asc-desc was happening when I click on header header id in table. And display the word asc or desc to the right of the header id. Table I export in file Home.js from file - Table.js.
What I need to change in file Table.js that implement sort when I click to header id?
Home.js:
import Table from "./Table/Table.js";
const Home = () => {
const [value, setValue] = useState({
listCategory: [],
sortAscDesc: "asc",
});
useEffect(() => {
async function fetchData(sortAscDesc) {
const res = await api('api/categories', sortAscDesc);
/....
}
fetchData(value.sortAscDesc);
}, [value.sortAscDesc]);
const changeSortAscDesc = () => {
setValue((prev) => ({
...prev,
sortAscDesc: prev.sortAscDesc == 'asc' ? 'desc' : 'asc'
}));
};
return (
<div>
<Table dataAttribute={value.listCategory}/>
// I WANT DELETE THIS BUTTON: - BECAUSE I WANT SORT BY HEADER "id"
<button onClick={() => changeSortAscDesc()}>changeAsc</button>
</div>
);
};
Table.js:
export default ({dataAttribute}) => (
<table className="table">
<thead className="table-head">
<tr>
<th>id</th> //I WANT SORT WHEN I CLICK ELEMENT id
<th>title</th>
<th>created_at</th>
</tr>
</thead>
<tbody>
{dataAttribute.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.created_at}</td>
</tr>
))}
</tbody>
</table>
);
You can try like this:
<Table dataAttribute={value.listCategory} changeSortAscDesc={changeSortAscDesc} />
In your Table.js
export default (props) => (
<table className="table">
<thead className="table-head">
<tr>
<th onClick={props.changeSortAscDesc}>id</th> //I want sort when I click by element id
<th>title</th>
<th>created_at</th>
</tr>
</thead>
<tbody>
{props.dataAttribute.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.created_at}</td>
</tr>
))}
</tbody>
</table>
);

Resources