How to keep the checkbox state saved after page refresh? - reactjs

I am making a product comparison page. I am trying to keep the checkbox checked after page refresh. Actually I have a products page where each product has a checkbox beneath it. When I click the checkbox, that specific product is added to local Storage + comparison page which I have made. But when I refresh the page, that product is saved but checkbox is unchecked but I want to keep checkbox checked and if checkbox is unchecked, that specific item should be removed from comparison page. How do I solve this query. I have tried several times but not able to do this?? Below is my code
function Home()
{
let history = useHistory()
const getLocalItems = () => {
let compare = localStorage.getItem('compare')
console.log(compare)
if(compare){
return JSON.parse(localStorage.getItem('compare'))
}
else{
return []
}
}
const [comparison,showcomparison] = useState(getLocalItems())
const [item,setItems] = useState()
const [show,setShow] = useState(false)
function onAdd(record){
const exist = comparison.find((x) => x.id === record.id)
if(exist){
showcomparison(comparison.map((x) => x.id === record.id ? {...exist, quantity: exist.quantity+1} : x)
);
}
else
{
showcomparison([...comparison,{...record,quantity: 1}])
}
}
useEffect(() => {
localStorage.setItem('compare',JSON.stringify(comparison))
}, [comparison])
const removeAll = () => {
showcomparison([])
}
return(
<div className="Home">
{
records.map(record => {
return(
<div className='container' key={record.id} onAdd = {onAdd}>
<div className='row'>
<div className='col-xl-3'>
<img style={{width: '100%', height: 'auto'}} src={record.img1} alt=""/><br></br>
<input type='checkbox' value={record.img1} onChange={() => onAdd(record)} style={{paddingRight: '30%'}}/>Compare
</div>
<div className='col-xl-4'>
<p style={{textAlign: 'left', fontWeight: 'bold', fontSize: '18px'}}>{record.title}</p>
<p style={{textAlign: 'left', fontWeight: 'bold', fontSize: '18px'}}>{record.title2}</p>
<p style={{textAlign: 'left', fontWeight: 'bold', fontSize: '18px'}}>{record.title3}</p>
<p style={{textAlign: 'left', fontSize: '15px'}}>MFG#: {record.MFG} | CDW#: {record.CDW}</p>
<p style={{fontWeight: '650', textAlign: 'left'}}>Laptop Type: {record.Type}</p>
<p style={{fontWeight: '650',textAlign: 'left'}}>Screen size: {record.size}</p>
<p style={{fontWeight: '650',textAlign: 'left'}}>Processor Type: {record.ptype}</p>
<p style={{fontWeight: '650',textAlign: 'left'}}>Processor Speed: {record.pspeed}</p>
<p style={{fontWeight: '650',textAlign: 'left'}}>Hard Drive Capacity: {record.capacity}</p>
</div>
<div className='col-xl-3'>
<ul>
<li style={{color: 'green', marginBottom: '1px', textAlign: 'left', fontSize: '13.5px', fontWeight: '640'}}><p>{record.Availability}</p></li>
</ul>
<p style={{fontSize: '13px', textAlign: 'left'}}>Ships today if ordered within 6 hrs 21 mins</p>
<h4 style={{textAlign: 'left', fontFamily: '"Source Serif Pro",serif', fontWeight: 'bold'}}>{record.price}</h4>
<p style={{textAlign: 'left'}}>Advertised Price</p>
<div className='input-group'>
<button type='button' onClick={handleDecrement} className='input-group-text'>-</button>
<div className="form-control text-center"> {quantity} </div>
<button type='button' onClick={handleIncrement} className='input-group-text'>+</button>
</div><br></br>
<button style={{width: '100%', background: '#150404', color: 'white', fontSize: '17.5px', fontWeight: '600', height: '18%'}}>Add to Cart</button>
</div>
</div><hr></hr>
</div>
)
})
}

You can use useRef on the input checkbox, use the following property to set it checked or unchecked.
checkRef.current.checked=false
<input type='checkbox' ref={checkRef} value={record.img1} onChange={() =>
onAdd(record)} style={{paddingRight: '30%'}}/>Compare
use the checkRef.current to change the value with useState.

Related

Unable to update react range values through input

I'm writing a code where I'm using react-range to create a range bar. along with it, I've got 2 textboxes that have the min and max values of the range. Currently, I'm able to update the range values by dragging and the text boxes get updated automatically. And I also want to update the price range using the textboxes mentioned, currently, the min box value works fine, but, when I try to update the max value, the value gets appended instead of updating. Unable to know where I'm going wrong.
Here is the code of the same.
import { useState } from "react";
import { Range, getTrackBackground } from "react-range";
export const PriceRange = () => {
const [priceValues, setPriceValues] = useState([1, 100]);
let step = 1;
const handleChange = (values: number[]) => {
if (values[0] > values[1]) {
values[0] = values[1];
}
if (values[1] < values[0]) {
values[1] = values[0];
}
setPriceValues(values);
};
return (
<>
{
<div
style={{
width: "50%",
maxWidth: "50%",
marginLeft: "auto",
marginRight: "auto"
}}
>
<div className="mb-8 text-gray-900 text-sm font-medium text-left">
Price
</div>
<div
style={{ padding: "1rem", marginTop: "1rem", marginBottom: "1rem" }}
>
<Range
step={step}
min={priceValues[0]}
max={priceValues[1]}
values={priceValues}
onChange={(values) => handleChange(values)}
renderTrack={({ props, children }) => (
<div
{...props}
style={{
...props.style,
background: getTrackBackground({
values: priceValues,
colors: ["#c4c4c442", "black", "#c4c4c442"],
min: priceValues[0],
max: priceValues[1]
}),
height: "0.5rem",
width: "100%"
}}
>
{children}
</div>
)}
renderThumb={({ index, props }) => (
<div
{...props}
style={{
...props.style
}}
className="thumbs"
>
<div className="priceValues">{`$${
priceValues[index] === priceValues.length - 1
? priceValues[index] + "+"
: priceValues[index]
}`}</div>
</div>
)}
/>
</div>
<div className="rangeBoxesContainer">
<div className="rangeBox">
<div style={{ display: "flex" }}>Min $</div>
<input
className="rangeInputbox"
type="number"
value={priceValues[0]}
onChange={(e) => {
if (Number(e.target.value) < Number(priceValues[1])) {
let newValue = [...priceValues];
newValue[0] = Number(e.target.value);
setPriceValues(newValue);
}
}}
/>
</div>
<div className="rangeBox">
<div style={{ display: "flex" }}>Max $</div>
<input
className="rangeInputbox"
type="number"
value={priceValues[1]}
onChange={(e) => {
if (Number(e.target.value) > Number(priceValues[1])) {
let newValue = [...priceValues];
newValue[1] = Number(e.target.value);
setPriceValues(newValue);
}
}}
/>
</div>
</div>
</div>
}
</>
);
};
And here is a working code sandbox link. Can someone let me know where I'm going wrong?
Thanks

How to close only one Div at a time in React?

Code:-
const [Close, setClose] = useState(true)
<div className="allDivs">
{item.map((item, index) => {
// console.log("myDivs", myDivs);
return (
<Fragment key={index} >
<div className="tableHeaderBody" id="CLOSEDIV" style={{display: Close ? 'initial' : 'none'}}>
<div className="TableText">
<div className="TableTextHide"></div> <div style={{ color: "white" }} id="SHOW">{item.val}</div></div>
<div className="CloseIcon" id="CloseBtn"><FaCircle style={{ color: "#FC0000", width: "10px", height: "10px", alignItems: "right" }} onClick={() => setClose(false)} /></div>
</div>
</Fragment>
)
})
}
</div>
I want that when i click the Red circle at any div (show in image) it close the div, but right now when i click the One div red circle its closes all the div
please help.
Try this:
Create a new component ChildComponent:
export default function ChildComponent({item}) {
const [Close, setClose] = useState(true) // Every Child now has it's own setClose controll
return (
<Fragment>
<div className="tableHeaderBody" id="CLOSEDIV" style={{display: Close ? 'initial' : 'none'}}>
<div className="TableText">
<div className="TableTextHide"></div>
<div style={{ color: "white" }} id="SHOW">{item.val}</div>
</div>
<div className="CloseIcon" id="CloseBtn">
<FaCircle style={{ color: "#FC0000", width: "10px", height: "10px", alignItems: "right" }} onClick={() => setClose(false)} />
</div>
</div>
</Fragment>
)
}
Pass the ChildComponent to your component shown above:
<div className="allDivs">
{item.map((item, index) => (
<div key={index}>
<ChildComponent item={item} />
</div>
))}
</div>

How to update height prop with useRef in React?

I need to dynamically define the size of HTML element and set its height to component. I tried to do this with useRef but it doesn't work as expected because of state which contains the previous value (not the current one). Could someone help me with this?
And here's the link: CodeSandBox https://codesandbox.io/s/happy-water-fzqk8?file=/src/App.js
The below code works fine but there's hardcored variable HEIGHT which defines the height of a tab. My task is to make the height dynamic
import { useState } from 'react';
const HEIGHT = {
0: 200,
1: 400,
2: 800,
}
function App() {
const [tab, setTab] = useState(0);
const switchTab = (id) => {
setTab(id);
};
return (
<div
style={{
margin: '100px auto',
backgroundColor: 'pink',
width: '400px',
overflow: 'hidden',
height: HEIGHT[tab], // need this to be dynamic not hardcored
}}
>
<div>
{tab === 0 && (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h2>Tab 1</h2>
<input />
<button onClick={() => switchTab(1)}>Go to tab 2</button>
<p>Some text here</p>
</div>
)}
{tab === 1 && (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h2>Tab 2</h2>
<input />
<button onClick={() => switchTab(0)}>Go to tab 1</button>
<button onClick={() => switchTab(2)}>Go to tab 3</button>
<p>
Some more text here. Some more text here. Some more text here. Some more text here.
Some more text here. Some more text here. Some more text here
</p>
</div>
)}
{tab === 2 && (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h2>Tab 3</h2>
<input />
<button onClick={() => switchTab(0)}>Go to tab 1</button>
<button onClick={() => switchTab(1)}>Go to tab 2</button>
</div>
)}
</div>
</div>
);
}
What I tried:
Added useRef and state which holds the element height
const elRef = useRef(0);
const [height, setHeight] = useState(elRef.current.offsetHeight);
Added function which calculates the size of an element and then sets it to state variable
const resizeHeight = useCallback(() => {
const size = elRef.current.offsetHeight;
setHeight(size)
}, [elRef]);
Added state Height to styles this way
<div
style={{
margin: '100px auto',
backgroundColor: 'pink',
width: '400px',
overflow: 'hidden',
height: height, // it should be the element size
}}
>
It doesn't work((
Here's the link...with the state height - undefined
https://codesandbox.io/s/objective-brown-zq7ih?file=/src/App.js
You can easily update your elRef reference in the switchTab handler without using useEffect and any useCallback hooks:
const elRef = useRef(0);
const SwitchTab = (id) => {
setTab(id);
setHeight(elRef.current.offsetHeight)
};
Now pass the elRef to the ref property of your target div:
return (
<div
style={{
margin: '100px auto',
backgroundColor: 'pink',
width: '400px',
overflow: 'hidden',
height: HEIGHT[tab],
}}
>
<div ref={elRef}> // ------------------------> added here
{tab === 0 && (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h2>Tab 1</h2>
<input />
<button onClick={() => switchTab(1)}>Go to tab 2</button>
<p>Some text here</p>
</div>
)}
{tab === 1 && (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h2>Tab 2</h2>
<input />
<button onClick={() => switchTab(0)}>Go to tab 1</button>
<button onClick={() => switchTab(2)}>Go to tab 3</button>
<p>
Some more text here. Some more text here. Some more text here. Some more text here.
Some more text here. Some more text here. Some more text here
</p>
</div>
)}
{tab === 2 && (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h2>Tab 3</h2>
<input />
<button onClick={() => switchTab(0)}>Go to tab 1</button>
<button onClick={() => switchTab(1)}>Go to tab 2</button>
</div>
)}
</div>
</div>
);

Toggling actions individually in cards mapped over

I have cards that are mapped over They all have a toggle button menu and Im trying to figure out how to target the menu's individually since they are controlled by a single part of the state Im not even sure this is possible. But I think it should be right? I have created a simple working example here. Im not sure if I can use the ID of them to target them individually but im not sure how to implement that. Any advice would be helpful thanks!
1) First of all you can't control all state of Items with single boolean state, You can create open as an array and then initially set it as false.
const [open, setOpen] = useState(Array.from(Items, () => false));
2) When you want to toggle particular element then you can use index
onClick={() => toggle(idx)}
3) Then you have to handle 2 cases where you update newOpenState
case: 1: when you update respective state after you click on the button. In this case you are just toggling the value which is present at the index idx
<Button
isOpen={open[idx]}
onClick={() => toggle( idx )}
style={{
width: "30px",
height: "30px",
marginTop: "25px",
marginLeft: "10px"
}}
/>
case 2: When you externally providing the value
<Col
md="12"
onClick={() => toggle( idx, false )}
className="editCol"
>
So these two cases are covered by using a single expression as:
newOpenState[index] = value ?? !newOpenState[index];
what above statement means is if the value is provided(case 2) then you just have to assign the value to newOpenState[index]. If you haven't provided the value(case 1) then it will be undefined, and you have to just toggle the value of newOpenState[index]. I've used using Nullish coalescing operator (??) you can assign the right hand side value of ?? if left hand side of value is undefined or null.
CODE
import "./styles.css";
import { Card, Button, Col, Row } from "reactstrap";
import { useState } from "react";
const Items = [
{
name: "Test 1",
ID: 1234
},
{
name: "Test 2",
ID: 4321
},
{
name: "Test 3",
ID: 3421
}
];
export default function App() {
const [open, setOpen] = useState(Array.from(Items, () => false));
const toggle = (index, value) => {
const newOpenState = [...open];
newOpenState[index] = value ?? !newOpenState[index];
setOpen(newOpenState);
};
return (
<>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
<div style={{ display: "flex", justifyContent: "space-between" }}>
{Items.map((item, idx) => (
<Card
key={idx}
style={{
border: "solid",
borderColor: "#00000",
margin: "5px",
width: "150px",
display: "flex",
justifyContent: "center"
}}
>
<h1>{item.name}</h1>
<span style={{ display: "flex" }}>
{!open[idx] ? (
<Button
isOpen={open[idx]}
onClick={() => toggle(idx)}
style={{
width: "30px",
height: "30px",
marginTop: "25px",
marginLeft: "10px"
}}
>
...
</Button>
) : (
<Card
style={{
border: "solid 1px",
borderColor: "#00000",
margin: "5px"
}}
>
<Row>
<Col md="12" className="closeMenu">
<span className="X" onClick={() => toggle(idx, false)}>
X
</span>
</Col>
</Row>
<Row>
<Col
md="12"
onClick={() => toggle(idx, false)}
className="editCol"
>
<span
className="editName"
onClick={() => setOpen(item.ID)}
>
Edit Name
</span>
</Col>
</Row>
<Row>
<Col md="12">
<span
className="deleteForm"
onClick={() => handleFormDelete(item.ID)}
>
Delete
</span>
</Col>
</Row>
</Card>
)}
</span>
</Card>
))}
</div>
</>
);
}

OnClick passing undefined

I am trying to pass an onClick value, but it passes undefined. Works well if it is not a Card/Paper/Div but I need this "button" to be a Card/paper/div
onClick={() => this.handleChange('bostadstyp')}
onClick={ value='villa', handleChange('bostadstyp')}
render() {
const { bostadstyp } = this.props;
const { auth } = this.props;
const { value, values, handleChange, handleClick } = this.props;
const { backgroundColori } = this.props;
const backgroundColor2 = "#F09515";
const aa = "";
// const imageUrl = window.innerWidth >= "1600px" ? Banner1 : Banner2;
// {if (!auth.uid) return <Redirect to="/loggain" />;}
return (
<div>
<img
className="stars3"
src={Logo}
width="15%"
align="center"
marginBottom="10px"
/>
<img src={Logo} width="15%" align="center" marginBottom="10px" />
<div style={{ backgroundColor: "#F1F1F1", padding: "30px" }}>
<h2
style={{
marginTop: "0px",
marginLeft: "10%",
color: "#F09515",
fontFamily: "Avenir",
fontWeight: "bold"
}}
>
{" "}
Dina Preferenser{" "}
</h2>
</div>
<form
className="background123"
style={{
backgroundColor: "#fdfdfd",
marginTop: "0px",
marginBottom: "0px"
}}
>
<Card
className="row xl12 l12 m12 s12"
style={{
marginLeft: "10%",
marginRight: "10%",
padding: "30px",
backgroundColor: "#fdfdfd"
}}
>
<br />
<div className="col xl6 l6 m12 s12">
<h4
style={{
fontFamily: "Avenir",
fontWeight: "bold",
color: "#F09515"
}}
>
1. Välj din boendestyp
</h4>
<p className="texti" style={{ fontSize: "16px" }}>
Vi behöver den här uppgift då vissa avtal endast gäller för
vissa anläggningstyper.
</p>
</div>
<div className="col xl6 l6 m12 s12">
<Card
className="row cardi xl12 l12 m12 s12"
style={{ width: "270%", backgroundColor: `white` }}
value={"villa"}
onClick={handleChange("bostadstyp")}
>
<div className=" col xl7 l7 m6 s6">
<img src={villa} width="50px" />
</div>
<div className="col xl5 l5 m5 s5">
<h6
className="texti"
style={{ fontSize: 16, textAlign: "right" }}
>
Villa/radhus
</h6>
</div>
</Card>
</div>
</Card>
</form>
</div>
);
}
I expect to 'values.bostadstyp' get the value 'villa'
because you are using function in the class so you must using this.handleChange instead of handleChange
<Card onClick={() => this.handleChange('bostadstyp' )}>
....
</Card>
You need to attach the onClick() listener to a <CardActionsArea/> component which should be nested inside your <Card/> component
Something like
<Card className="row cardi xl12 l12 m12 s12" style={{ width: '270%', backgroundColor: `white` }} value={'villa'} onClick={handleChange('bostadstyp' )} >
<div className=" col xl7 l7 m6 s6">
<img src={villa} width="50px" />
</div>
<div className="col xl5 l5 m5 s5">
<h6 className="texti" style={{ fontSize: 16, textAlign: "right" }}>Villa/radhus</h6>
</div>
</Card>
Also, I suggest looking into the <CardActions/> documentation, the example here shows how its used
Can you try below code in your card element:
<Card className="row cardi xl12 l12 m12 s12"
style={{ width: '270%', backgroundColor: `white` }} value={'villa'}
onClick={(e) => {
e.preventDefault();
this.handleChange('bostadstyp');
}}>
The issue may be due to one of the below reasons:
1. handleChange() not bound with this.
2. To pass values from onClick handler you should use (e) => func('value')
3. The click is happening inside form element, its better to include event.preventDefault()
You need user arrow function:
onClick={() => { this.handleChange('bostadstyp' ) }}
<Card className="row cardi xl12 l12 m12 s12" style={{ width: '270%', backgroundColor: `white` }} value={'villa'} onClick={() => { this.handleChange('bostadstyp' ) }} >
<div className=" col xl7 l7 m6 s6">
<img src={villa} width="50px" />
</div>
<div className="col xl5 l5 m5 s5">
<h6 className="texti" style={{ fontSize: 16, textAlign: "right" }}>Villa/radhus</h6>
</div>
</Card>
UPDATE
Handle Change from parent component:
Make sure your handleChange is bound properly by explicitly binding it or using an arrow function syntax:
this.handleChange = this.handleChange.bind(this) // In component's constructor
// or
handleChange = (value, input) => {
this.setState({
[input]: value
});
};
onClick expects a function to invoke, so you need to wrap your handler into one:
<Card onClick={() => handleChange('villa', 'bostadstyp')}></Card>

Resources