Having problem in reset of checkboxes in react functional component want solution? - javascript

//Main functional component
function CustomizedTables(props) {
const classes = useStyles();
const [nameCheckbox, setnameCheckbox] = useState([]);
const [emailCheckbox, setemailCheckbox] = useState([]);
const [faxCheckbox, setfaxCheckbox] = useState([]);
const [loopStopFlag, setFlag] = useState(true);
const [emailFlag, setEmailFlag] = useState(false);
const rows = props.data;
//Initialization of checkboxes to true
if (loopStopFlag) {
let nameArray = [];
let emailArray = [];
let faxArray = [];
rows.SupplierList.forEach((supplier, i) => {
nameArray.push(true);
if (supplier.Email) {
emailArray.push(true);
} else {
emailArray.push(null);
}
if (supplier.Fax) {
faxArray.push(true);
} else {
faxArray.push(null);
}
});
setnameCheckbox(nameArray);
setemailCheckbox(emailArray);
setfaxCheckbox(faxArray);
setFlag(false);
}
//Reseting all checkboxes
const resetAllCheckbox = () => {
let nameArray = [];
let emailArray = [];
let faxArray = [];
rows.SupplierList.forEach((supplier, i) => {
nameArray.push(false);
if (supplier.Email) {
emailArray.push(false);
} else {
emailArray.push(null);
}
if (supplier.Fax) {
faxArray.push(false);
} else {
faxArray.push(null);
}
});
setnameCheckbox(nameArray);
setemailCheckbox(emailArray);
setfaxCheckbox(faxArray);
if (emailCheckbox.includes(true)) {
setEmailFlag(false);
} else {
setEmailFlag(true);
}
};
//Displaying data on screen
return (
<div>
<Paper className={classes.root} style={{ marginLeft: "32px" }}>
<Table className={classes.table} aria-label="customized table">
<TableBody>
{rows.SupplierList.map((row, i) => (
<StyledTableRow key={i}>
<StyledTableCell style={{ color: "#474747" }} align="center">
<label>
{row.Name}{" "}
<input
type="checkbox"
style={{ marginLeft: "5px", cursor: "pointer" }}
onChange={() => {
let arr = nameCheckbox;
arr[i] = !arr[i];
setnameCheckbox(arr);
}}
defaultChecked={nameCheckbox[i]}
/>
</label>
<br />
<div style={{ fontSize: "11px" }}>{row.Gbf && "GBF"}</div>
</StyledTableCell>
<StyledTableCell align="center" style={{ color: "#474747" }}>
{row.Email && (
<React.Fragment>
<input
type="checkbox"
style={{ marginLeft: "5px", cursor: "pointer" }}
onChange={() => {
let arr1 = emailCheckbox;
arr1[i] = !arr1[i];
setemailCheckbox(arr1);
if (emailCheckbox.includes(true)) {
setEmailFlag(false);
} else {
setEmailFlag(true);
}
}}
defaultChecked={emailCheckbox[i]}
/>
</React.Fragment>
)}
</StyledTableCell>
<StyledTableCell align="center" style={{ color: "#474747" }}>
{row.Fax && (
<React.Fragment>
<input
type="checkbox"
style={{ marginLeft: "5px", cursor: "pointer" }}
onChange={() => {
let arr2 = faxCheckbox;
arr2[i] = !arr2[i];
setfaxCheckbox(arr2);
}}
value={faxCheckbox[i]}
defaultChecked={faxCheckbox[i]}
/>
</React.Fragment>
)}
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</Paper>
<br />
<Button
variant="danger"
style={{ width: "100px" }}
onClick={resetAllCheckbox}
>
Reset
</Button>
</div>
);
}
export default CustomizedTables;
I am facing problem in reseting checkboxes. When I reset them only array is updating but no action is performing or checkboxes. I used both checked and defaultChecked in checked also facing problem that bool is updating but checkbox is not updating. I want a solution for it. Actually when I click on reset button then my checkboxes remain same form but state is updating

I couldn't replicate your exact code in a sandbox because of it's dependencies so I've put up a simple example that has a set of checkboxes, check action and reset action.
Modify your code based on the example and these notes and it would work as expected:
Whenever you render a react element from inside loops you should provide a unique key, this helps react identify what changed.
Keep your checkbox state in your react/store state and set it using checked prop and not defaultChecked. On reset update the store/local states and the component will re-render.
Check the sandbox:
https://codesandbox.io/s/pedantic-violet-twtvn?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
function CheckboxDemo({ data }) {
const [features, setFeatures] = useState([]);
useEffect(() => {
setFeatures(data);
}, [data]);
const resetCheckboxes = () => {
const newFeatures = [];
features.forEach(f => {
newFeatures.push({ ...f, checked: false });
});
setFeatures(newFeatures);
};
const setFeatureCheck = index => () => {
const updatedFeature = {
...features[index],
checked: !features[index].checked
};
const newFeatures = [
...features.slice(0, index),
updatedFeature,
...features.slice(index + 1)
];
setFeatures(newFeatures);
};
const checkBoxes = features.map((f, i) => (
<div className="box" key={i + "_" + f.label}>
<input
type="checkbox"
checked={f.checked}
onChange={setFeatureCheck(i)}
/>{" "}
{f.label}
</div>
));
return (
<div className="wrapper">
{checkBoxes}
<div className="controls">
<input type="button" onClick={resetCheckboxes} value="Reset" />
</div>
</div>
);
}
export default CheckboxDemo;

Related

setState not updating value with object

This is my initial state:
const [inputList, setInputList] = useState([
{ answer: "Answer 1", isCorrect: false, points: 0 },
]);
I am adding input field dynamically and initially there will be one field. There is an Add button through which I can add more fields and remove button with that I can remove fields. But the problem I'm facing, after submitting the value I want to clear all the fields and there should be only one field as it was initially.Suppose if I add 4 fields and after submitting there should be only one. But when I'm trying to do that its not working but add, remove and even setting empty the state is working but setting only one field is not working.I'm mapping depending on inputList value but still after submitting I'm getting all the fields. If I have added 4 fields then after submitting also its 4. The state value is not being changed at all.
ADD more function:
const handleAddClick = () => {
let count = inputList.length;
let newfield = {
answer: `Answer ${count + 1}`,
isCorrect: false,
points: 0,
};
setInputList([...inputList, newfield]);
};
remove function:
const handleRemoveClick = (index) => {
let list = [...inputList];
let newList = list.filter((item, ind) => ind !== index);
setInputList(newList);
};
submit function: This should reset the state to initial state that is only one field should be there But not working
const Save = () => {
setInputList([{ answer: "Answer 1", isCorrect: false, points: 0 }])
}
I tried to do loop also for deleting every index but it only removes one no matter how many times the loop runs
Whole Component:
import DeleteIcon from "#mui/icons-material/Delete";
import React from "react";
import Checkbox from "#mui/material/Checkbox";
import Radio from "#mui/material/Radio";
import FormControl from "#mui/material/FormControl";
import IconButton from "#mui/material/IconButton";
import Switch from "#mui/material/Switch";
import TextField from "#mui/material/TextField";
import { useState } from "react";
import PlusSign from "../../../../Files/img/plus.png";
import Header from "../../../Header/Header";
import Topbar from "../../../Topbar/Topbar";
import styles from "../MultipleResponse/MultipleResponse.module.scss";
import QuestionEditor from "../../Editor/QuestionEditor/QuestionEditor";
import ReactTag from "../../TagInput/ReactTag";
import FormControlLabel from "#mui/material/FormControlLabel";
import { styled } from "#mui/material/styles";
import axios from "axios";
import "react-quill/dist/quill.snow.css";
import AnswerEditor from "../../Editor/AnswerEditor/AnswerEditor";
import SelectComp from "../../SelectComp/SelectComp";
import {
timeOptions,
categoryOptions,
difficultyLevelOptions,
} from "../../../../utils/constants";
import { RadioGroup } from "#mui/material";
import Snackbars from "../../../Assesment/MyAssesment/Snackbar";
import { useEffect } from "react";
export default function MultipleChoice() {
const baseURL = "http://localhost:5000/api/question";
const [distributePoints, setDistributePoints] = useState(false);
const [inputList, setInputList] = useState([
{ answer: "Answer 1", isCorrect: false, points: 0 },
]);
const [question, setQuestion] = useState("");
const [tags, setTag] = useState([]);
const [title, setTitle] = useState("");
const [time, setTime] = useState("");
const [difficultyLevel, setdifficultyLevel] = useState("");
const [category, setCategory] = useState("");
const [whatToLook, setwhatToLook] = useState("");
const [questionRelevent, setQuestionRelevent] = useState("");
const [shuffle, setShuffle] = useState(false);
const [value, setValue] = React.useState("");
const [loading, setLoading] = useState(false);
const [loading2, setLoading2] = useState(false);
const [snackText, setSnackText] = useState("");
const [severity,setSeverity]=useState("")
useEffect(() => {
const items = JSON.parse(localStorage.getItem("tempData"));
if (items) {
setTitle(items[0].title);
setdifficultyLevel(items[0].level);
setwhatToLook(items[0].whatToLookFor);
setQuestionRelevent(items[0].whyQuestionIsRelevant);
setTag(items[0].tags);
setTime(items[0].time);
setInputList(items[0].answers);
setCategory(items[0].category);
setQuestion(items[0].detail);
setShuffle(items[0].shuffleAnswers);
setDistributePoints(items[0].distributePoints);
setValue(items[0].radioValue);
}
}, []);
const [snack, setSnack] = React.useState(false);
const closeSnackbar = (event, reason) => {
if (reason === "clickaway") {
return;
}
setSnack(false);
};
const IOSSwitch = styled((props) => (
<Switch
focusVisibleClassName=".Mui-focusVisible"
disableRipple
{...props}
/>
))(({ theme }) => ({
width: 40,
height: 24,
padding: 0,
marginRight: 10,
"& .MuiSwitch-switchBase": {
padding: 0,
margin: 2,
transitionDuration: "300ms",
"&.Mui-checked": {
transform: "translateX(16px)",
color: "#fff",
"& + .MuiSwitch-track": {
backgroundColor:
theme.palette.mode === "dark" ? "#3699FF" : "#3699FF",
opacity: 1,
border: 0,
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: 0.5,
},
},
"&.Mui-focusVisible .MuiSwitch-thumb": {
color: "#33cf4d",
border: "6px solid #fff",
},
"&.Mui-disabled .MuiSwitch-thumb": {
color:
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[600],
},
"&.Mui-disabled + .MuiSwitch-track": {
opacity: theme.palette.mode === "light" ? 0.7 : 0.3,
},
},
"& .MuiSwitch-thumb": {
boxSizing: "border-box",
width: 20,
height: 20,
backgroundColor: "#FFFFFF",
},
"& .MuiSwitch-track": {
borderRadius: 26 / 2,
backgroundColor: theme.palette.mode === "light" ? "#E9E9EA" : "#39393D",
opacity: 1,
transition: theme.transitions.create(["background-color"], {
duration: 500,
}),
},
}));
const switchHandler = (event) => {
setDistributePoints(event.target.checked);
};
const handleInputChange = (e, index) => {
const list = [...inputList];
list[index]["answer"] = e;
setInputList(list);
};
const handlePointChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
const handleRemoveClick = (index) => {
let list = [...inputList];
let newList = list.filter((item, ind) => ind !== index);
setInputList(newList);
};
const handleAddClick = () => {
let count = inputList.length;
let newfield = {
answer: `Answer ${count + 1}`,
isCorrect: false,
points: 0,
};
setInputList([...inputList, newfield]);
};
const SaveAndAddAnother = () => {
setLoading2(true);
const newInputList = inputList.map(({ points, ...rest }) => {
return rest;
});
try {
axios
.post(baseURL, {
questionType: "MULTIPLE_CHOICE",
time: time.toString(),
title: title,
level: difficultyLevel,
detail: question,
category: category,
tags: tags,
whatToLookFor: whatToLook,
whyQuestionIsRelevant: questionRelevent,
answers: distributePoints ? inputList : newInputList,
distributePoints: distributePoints,
shuffleAnswers: shuffle,
})
.then((response) => {
setLoading(false);
if (response.data.result === 1) {
setSnack(true);
setSeverity('success')
setTimeout(() => {
setLoading2(false);
}, 1000);
setSnackText("Question added successfully");
setTime("");
setTitle("");
setdifficultyLevel("");
setQuestion("");
setCategory("");
setTag([]);
setwhatToLook("");
setQuestionRelevent("");
setDistributePoints(false);
setShuffle(false);
setValue("A");
let newList = [{ answer: "Answer 1", isCorrect: false, points: 0 }];
setInputList(newList);
localStorage.removeItem("tempData");
} else {
setLoading(false);
}
})
.catch((err) => {
// Handle error
setLoading2(false);
setSnack(true)
setSnackText("Error Adding question")
setSeverity('error')
});
} catch (e) {
setLoading(false);
}
};
const Save = () => {
setInputList([{ answer: "Answer 1", isCorrect: false, points: 0 }])
// setSnack(true);
// setSnackText("Question Saved as draft");
// setSeverity('success')
// setLoading(true);
// const localData = [
// {
// questionType: "MULTIPLE_CHOICE",
// time: time.toString(),
// title: title,
// level: difficultyLevel,
// detail: question,
// category: category,
// tags: tags,
// whatToLookFor: whatToLook,
// whyQuestionIsRelevant: questionRelevent,
// answers: inputList,
// distributePoints: distributePoints,
// shuffleAnswers: shuffle,
// radioValue: value,
// },
// ];
// localStorage.setItem("tempData", JSON.stringify(localData));
// setTimeout(() => {
// setLoading(false);
// }, 2000);
};
const questionText = (value) => {
setQuestion(value);
};
const selectedTags = (tags) => {
setTag(tags);
};
const handleTitle = (e) => {
setTitle(e.target.value);
};
const handleTime = (e) => {
setTime(e.target.value);
};
const handleDifficulty = (e) => {
setdifficultyLevel(e.target.value);
};
const handleCategory = (e) => {
setCategory(e.target.value);
};
const handleWhatToLookFor = (e) => {
setwhatToLook(e.target.value);
};
const handleQuestionRelevent = (e) => {
setQuestionRelevent(e.target.value);
};
const handleShuffle = (event) => {
setShuffle(event.target.checked);
};
function handleRadioButton(event, i) {
if (event.target.value === value) {
setValue("");
} else {
setValue(event.target.value);
}
const list = [...inputList];
Object.keys(list).forEach((key) => {
list[key]["isCorrect"] = false;
});
list[i]["isCorrect"] = true;
setInputList(list);
}
return (
<div className={styles.mainContainer}>
<Header />
<Topbar
questionType="Multiple-response"
loading={loading}
loading2={loading2}
SaveAndAddAnother={SaveAndAddAnother}
save={Save}
/>
<div className={styles.Container}>
<div className={styles.questionContainer}>
<div className={styles.left}>
<p className={styles.question}>Question</p>
<div className={styles.input}>
<TextField
variant="standard"
InputProps={{
disableUnderline: true,
}}
className={styles.title}
placeholder="Title.."
value={title}
onChange={(e) => handleTitle(e)}
/>
<QuestionEditor
question={question}
questionText={questionText}
theme="snow"
/>
</div>
<div className={styles.category}>
<ReactTag tags={tags} selectedTags={selectedTags} />
</div>
</div>
<div className={styles.right}>
<div className={styles.rightText}>
<span className={styles.heading}>Select the right answer</span>
<div>
<IOSSwitch
checked={distributePoints}
onChange={switchHandler}
/>
<span className={styles.instructinHeading}>
Distribute points across answers
</span>
</div>
</div>
<div className={styles.answers}>
<div className={styles.radio}>
<FormControl style={{ display: "flex", flex: 1 }}>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
>
{inputList.map((x, i) => {
return (
<div key={i}>
<div key={i} className={styles.options}>
{!distributePoints ? (
<FormControlLabel
key={i}
value={x.answer}
control={
<Radio
onClick={(e) => handleRadioButton(e, i)}
/>
}
defaultChecked={false}
/>
) : (
""
)}
<div className="editor">
<AnswerEditor
handleAnswer={handleInputChange}
index={i}
theme="bubble"
val={x.answer}
/>
</div>
{distributePoints ? (
<div className={styles.inputCounter}>
<TextField
variant="standard"
name="points"
InputProps={{
disableUnderline: true,
type: "number",
}}
inputProps={{
min: 0,
style: { textAlign: "center" },
}}
value={x.points}
onChange={(e) => handlePointChange(e, i)}
/>
</div>
) : (
""
)}
{inputList.length > 1 && (
<IconButton
onClick={() => handleRemoveClick(i)}
className={styles.icon}
>
<DeleteIcon
sx={{ fontSize: 24, color: "#3699FF" }}
/>
</IconButton>
)}
</div>
{inputList.length - 1 === i && (
<div className={styles.bottomItemContainer}>
{distributePoints ? (
<div className={styles.pointsInfoText}>
Allocate points across answers. Give the best
answer 5 points
</div>
) : (
""
)}
{!inputList.some((el) => el.isCorrect === true) &&
!distributePoints ? (
<div className={styles.pointsInfoText}>
Select the correct answer
</div>
) : (
""
)}
<div
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<button
onClick={handleAddClick}
type="button"
className={styles.addButton}
>
<img src={PlusSign} alt="" /> Add another
answer
</button>
<div className={styles.label}>
<Checkbox
checked={shuffle}
onChange={handleShuffle}
style={{ color: "#00A3FF" }}
/>
Shuffle answer
</div>
</div>
</div>
)}
</div>
);
})}
</RadioGroup>
</FormControl>
</div>
</div>
</div>
</div>
<div className={styles.itemContainer}>
<div className={styles.timeContainer}>
<SelectComp
handleChange={handleTime}
title="Time to answer the question"
val={time}
optionData={timeOptions}
/>
</div>
<div className={styles.timeContainer}>
<SelectComp
handleChange={handleCategory}
title="Question Category"
val={category}
optionData={categoryOptions}
/>
</div>
</div>
<div className={styles.itemContainer}>
<div style={{ flex: 1 }}>What to look for in the answer?</div>
<div style={{ flex: 1 }}>Why is this question relevant?</div>
</div>
<div className={styles.itemContainer}>
<div className={styles.releventText}>
<TextField
variant="standard"
InputProps={{
disableUnderline: true,
}}
className={styles.text}
placeholder="What to look for in the answer..."
value={whatToLook}
onChange={(e) => handleWhatToLookFor(e)}
/>
</div>
<div className={styles.releventText}>
<TextField
variant="standard"
InputProps={{
disableUnderline: true,
}}
className={styles.text}
placeholder="Why is this question relevant.."
value={questionRelevent}
onChange={(e) => handleQuestionRelevent(e)}
/>
</div>
</div>
<div className={styles.itemContainer}>
<div className={styles.difficultyLevel}>
<SelectComp
handleChange={handleDifficulty}
title="Difficulty Level"
val={difficultyLevel}
optionData={difficultyLevelOptions}
/>
</div>
{/* <div style={{ flex: 1 }}>Why is this question relevant?</div> */}
</div>
<div className={styles.bottomInfo}>
<div className={styles.element}>
<div className={styles.circle}></div>
<div className={styles.text}>
How should I formate my questions?
</div>
</div>
<div className={styles.element}>
<div className={styles.circle2}></div>
<div className={styles.text}>
{" "}
How do I use the question editor?
</div>
</div>
<div className={styles.element}>
<div className={styles.circle3}></div>
<div className={styles.text}>How do I use the formula editor?</div>
</div>
</div>
</div>
{snack && (
<Snackbars
text={snackText}
snack={snack}
closeSnackbar={closeSnackbar}
severity={severity}
/>
)}
{/* <div style={{ marginTop: 20 }}>{JSON.stringify(inputList)}</div> */}
</div>
);
}

How to add and delete the specific item from an array of an object in react.js?

I am trying to add a functionality wherein when a user clicks on add channel button he can add the channel and it will show the newly added channel name in the respective teams. Similarly, when a user wants to delete a channel he can delete it so by clicking on delete button.
However, I figured out most of the code but seems like when am updating my newchannel list using useState it is showing me the error saying teams.channels.map is undefined and can not read properties.
If anyone can help on this would really be helpful and appreciated.
Please find the source code link below
Codesandbox link
You should create a InputAddChannel component
const InputAddChannel = ({ handleAddChannel }) => {
const [inputChannelValue, setInputChannelValue] = useState("");
const handlInputChange = (event) => {
event.preventDefault();
const newChannelName = event.target.value;
setInputChannelValue(newChannelName);
};
return (
<>
<input
placeholder="add channel"
value={inputChannelValue}
onChange={handlInputChange}
/>
<button
disabled={!inputChannelValue}
onClick={() => handleAddChannel(inputChannelValue)}
>
Add channel
</button>
</>
);
};
and pass handleAddChannel function to it
const App = () => {
const [newchannel, setNewChannel] = useState(teams);
const [addteam, setAddteam] = useState("");
const handleAddChannel = (team, i) => {
const newData = newchannel.map((channel, index) => {
if (i === index)
return {
...channel,
channels: [
...channel.channels,
{
channelName: team,
channelid:
channel.channels[channel.channels.length - 1]?.channelid + 1 ||
1
}
]
};
return channel;
});
setNewChannel(newData);
};
const handleDeleteChannel = (cid, teamid) => {
const newData = newchannel.map((channel, index) => {
if (index === teamid)
return {
...channel,
channels: channel.channels.filter((c, i) => i !== cid)
};
return channel;
});
setNewChannel(newData);
};
const handleAddteam = (event) => {
event.preventDefault();
const addteaminput = event.target.value;
setAddteam(addteaminput);
};
const handlSubmitAddteam = () => {
const newaddedteam = newchannel.concat({
name: addteam,
channels: [
{
channelid: newchannel.length + 1,
channelName: "new"
}
]
});
setNewChannel(newaddedteam);
};
return (
<>
<div className="App">
{newchannel &&
newchannel.map((team, i) => (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "0.5rem",
alignContent: "center",
justifyContent: "center",
border: "1px solid black",
padding: "0.5rem"
}}
key={i}
>
<h1> {team.name} </h1>
<InputAddChannel
handleAddChannel={(value) => handleAddChannel(value, i)}
/>
<div>
{team.channels.map((c, cid) => (
<div
style={{
display: "flex",
gap: "1rem",
alignItems: "center",
justifyContent: "center",
border: "1px solid lightgray"
}}
key={cid}
>
<h6> {c.channelName}</h6>
<button
style={{ width: "5rem", height: "2rem" }}
onClick={() => handleDeleteChannel(cid, i)}
>
Delete
</button>
</div>
))}
</div>
</div>
))}
<input
placeholder="add team"
value={addteam}
onChange={handleAddteam}
/>
<button disabled={!addteam} onClick={handlSubmitAddteam}>
Add team
</button>
</div>
</>
);
};
You can check in my codesandbox. Hope it help!

Form resets stored components upon submission

I want to make an editable piece of display text, such that:
The text itself is not a textarea/input, it is just standard text
Clicking on the text will open a text area and edit button, which allows you to edit the text
Clicking the edit button will update the display text, and hide the textarea/edit button (display:none)
I have implemented every part of this, and now have a bug where any time I hit the 'edit' button, all of the text components are erased from the page entirely.
To achieve this, I have a Page component which stores a series of EditableText components (you can add more EditableText components via a + button at the bottom of the page):
const Page = (props) => {
const classes = useStyles()
var [components, setComponents] = useState([])
var [displayButton, setDisplayButton] = useState(false)
const toggleButton = () => {
displayButton = !displayButton
setDisplayButton(displayButton)
}
const addCaution = () => {
setComponents(components.concat(<Caution />))
displayButton = !displayButton
setDisplayButton(displayButton)
}
const addImportant = () => {
setComponents(components.concat(<Important />))
displayButton = !displayButton
setDisplayButton(displayButton)
}
const opbutton = (
<div>
<Button onClick={addHeader}>header</Button>
<Button onClick={addText}>text</Button>
<Button onClick={addCaution}>caution</Button>
<Button onClick={addImportant}>important</Button>
</div>
)
return (
<div className={classes.page}>
{components}
{displayButton ? opbutton : null}
<Button onClick={toggleButton}>+</Button>
</div>
)
The EditableText components are added into an array via useState.
Here is the code for the EditableText:
const EditableText = (props) => {
const classes = useStyles()
const { inputs, handleInputChange, handleSubmit } = useSubmit()
var [displayText, setDisplayText] = useState("click here to add notes!")
var [showBox, setShowBox] = useState(false)
var [showClickArea, setClickArea] = useState(true)
const inputBox = (
<form
onSubmit={handleSubmit}
style={{ display: "flex", flexDirection: "row", width: "100%" }}
>
<textarea
type="text"
name="displayText"
className={classes.textbox}
onChange={handleInputChange}
value={inputs}
style={{ borderColor: props.border }}
onSubmit={"return inputs"}
>
{inputs}
</textarea>
<Button id="editbutton" type="submit" className={classes.button}>
edit
</Button>
</form>
)
const toggleEdit = () => {
showClickArea = !showClickArea
setClickArea(showClickArea)
showBox = !showBox
setShowBox(showBox)
}
const clickArea = (
<div
style={{ width: "80%", height: "200%", position: "absolute" }}
onClick={toggleEdit}
/>
)
return (
<div>
{showClickArea ? clickArea : null}
<div className={classes.background} style={{ color: props.color }}>
{inputs}
<div>{showBox ? inputBox : displayText}</div>
</div>
</div>
)
}
You may notice the use of useSubmit which is a custom hook:
const useSubmit = (callback) => {
const [input, setInput] = useState({})
const handleSubmit = (event) => {
callback()
}
const handleInputChange = (event) => {
event.persist()
setInput((input) => ({
...input,
[event.target.name]: [event.target.value],
}))
}
return (handleSubmit, handleInputChange, input)
}
What I figure is that this may be an issue with the custom hook, or the use of form and submit. I think form is supposed to clear the page once submitted. But I'm not entirely sure. Any idea how to prevent this?
Solved by creating a function and an 'edit' variable:
function setDisplayState() {
if (edit === false) {
return (
<div
style={{
color: props.color,
fontWeight: props.weight ? props.weight : 400,
fontSize: props.size ? props.size : 14,
}}
>
{displayText}
</div>
)
} else {
return (
<div>
<textarea
className={classes.textbox}
style={{ color: props.color }}
value={displayText}
onChange={(e) => {
displayText = e.target.value
setDisplayText(displayText)
}}
/>
<Button className={classes.button} onClick={saveChange}>
save
</Button>
</div>
)
}
}

How can I add onChange to my onClick function?

Been struggling to try and solve this one,
I have a <form> that is wrapped around a Controller using a render. The render is a list of <Chip>'s. I want the list of chips to act as a field, each user toggled chip would act as the data inputted to the form, based on the label value of a chip. The expected data can be either one string or an array of strings based on how many <Chips> are toggled.
I've tried to replicate examples that use <Checkbox>, but I wasn't able to re-create the component.
I have a state of total chips toggled, which re-produces the data I would like to submit via my button. Is there a way to pass this state to my form data?
I'm currently only able to submit empty data through my form because my chip values and form data are not linked up correctly.
To solve this I need to add onChange to my onClick parameter for <Chip>, I've tried doing this:
onClick={(value, e) => { handleToggle(value); onChange(value) }}
But that does not work?
Here is my Form
<form noValidate onSubmit = { handleSubmit(onSubmit) }>
<Controller
render={({ onChange ,...props }) => (
<Paper component="ul" className={classes.root}>
{stocklist.map((value, index) =>
{
return (
<li key={index}>
<Chip
name="stock_list"
variant="outlined"
label={value}
key={index}
color={checked.includes(value) ? 'secondary' : 'primary'}
onClick={handleToggle(value)}
className={classes.chip}
ref={register}
/>
</li>
);
})}
</Paper>
)}
name="stock_list"
control={control}
defaultValue={[]}
onChange={([, data]) => data}
/>
{checked && checked.length > 0 &&
<Fab
color="primary"
aria-label="delete"
type="submit"
>
<DeleteIcon />
</Fab>
}
</form>
Here is how I toggle the chips, and create a state checked which holds the values for every toggled chip
const { register, control, handleSubmit } = useForm();
const [checked, setChecked] = React.useState([]);
const handleToggle = (value) => () => {
const currentIndex = checked.indexOf(value);
const newChecked = [...checked];
if (currentIndex === -1) {
newChecked.push(value);
} else {
newChecked.splice(currentIndex, 1);
}
setChecked(newChecked);
};
Here is my onSubmit function
const onSubmit = (data, e) =>
{
console.log(data);
axiosInstance
.patch('url', {
stock_list: data.stock_list,
})
.then((res) =>
{
console.log(res);
console.log(res.data);
});
};
Check it out https://codesandbox.io/s/cocky-roentgen-j0pcg Please toggle one of the chips, and then press the button that appears. If you then check the console, you will notice an empty form array being submitted.
I guess this will work for you
import React, { useState } from "react";
import { Chip, Paper, Fab, Grid } from "#material-ui/core/";
import { makeStyles } from "#material-ui/core/styles";
import { Controller, useForm } from "react-hook-form";
import DeleteIcon from "#material-ui/icons/Delete";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
justifyContent: "center",
flexWrap: "wrap",
listStyle: "none",
padding: theme.spacing(0.5),
margin: 0,
"#media (max-width: 600px)": {
overflowY: "auto",
height: 200
}
},
chip: {
margin: theme.spacing(0.5)
}
}));
export default function app() {
const { register, control, handleSubmit } = useForm();
const classes = useStyles();
const [checked, setChecked] = React.useState([]);
const stocklist = ["AAPL", "AMD", "TSLA"];
const onSubmit = (data, e) => {
console.log("data.stock_list");
console.log(data.stock_list);
console.log("data.stock_list");
};
const handleToggle = (value) => {// <== I changed this part
const currentIndex = checked.indexOf(value);
const newChecked = [...checked];
if (currentIndex === -1) {
newChecked.push(value);
} else {
newChecked.splice(currentIndex, 1);
}
setChecked(newChecked);
return newChecked;// <== I changed this part
};
return (
<>
{(!stocklist || stocklist.length === 0) && (
<p style={{ textAlign: "center" }}>Your Bucket is empty...</p>
)}
{stocklist && stocklist.length > 0 && (
<Grid>
<form noValidate onSubmit={handleSubmit(onSubmit)}>
<Controller
name="stock_list"// <== I changed this part
render={({ onChange, ...props }) => (
<Paper component="ul" className={classes.root}>
{stocklist.map((value, index) => {
return (
<li key={index}>
<Chip
// name="stock_list"// <== I changed this part
variant="outlined"
label={value}
key={index}
color={
checked.includes(value) ? "secondary" : "primary"
}
onClick={(e) => {
onChange(handleToggle(value));// <== I changed this part
}}
className={classes.chip}
ref={register}
/>
</li>
);
})}
</Paper>
)}
control={control}
defaultValue={{}}
onChange={([, data]) => data}
/>
{checked && checked.length > 0 && (
<Fab color="primary" aria-label="delete" type="submit">
<DeleteIcon />
</Fab>
)}
</form>
</Grid>
)}
</>
);
}

Increment React Counters in a mapped list

I want to be able to increment and decrement values using a single onChange and handleIncrement in a mapped functional component.
when the click function runs it updates all the listed inputs with the same value.
please, how can i get them to update each individually?
I am still quite the noob.
here is my code:
const [state, setState] = useState({
count: 0,
});
const handleChange = (e) => {
setState({
...state.count,
[e.target.name]: e.target.value,
});
};
const handleIncrement = () => {
setState((prevState) => {
return { count: prevState.count + 1 };
});
};
const listings = Consumables.map((list) => (
<Col lg={4}>
<Bounce up>
<Card
key={list.product}
style={{
width: "100%",
height: "25rem",
marginTop: "5%",
textAlign: "center",
}}
>
<Card.Header as="h3">{list.product}</Card.Header>
<Card.Body>
{list.productImage}
<br />
<br />
{list.description}
<br />
<br />
{list.weight}
<h5>Price: {list.price}</h5>
</Card.Body>
<Card.Footer>
<InputGroup className="mb-3">
<InputGroup.Prepend>
<Button variant="danger">-</Button>
</InputGroup.Prepend>
<FormControl
name={list.product}
type="text"
value={state.count}
onChange={handleChange}
/>
<InputGroup.Append>
<Button variant="success" onClick={handleIncrement}>
+
</Button>
</InputGroup.Append>
</InputGroup>
</Card.Footer>
</Card>
</Bounce>
</Col>
));
You just need to get the index of your counter to be able to increment it individually :
import React, { Component } from "react";
import { render } from "react-dom";
const App = () => {
const [counters, setCounters] = React.useState([0, 0, 0, 0]);
const setCount = (index, newCounter) => {
const newCounters = Object.assign([], counters);
newCounters[index] = newCounter;
setCounters(newCounters);
};
return (
<div>
{counters.map((counter, index) => (
<Counter index={index} counter={counter} setCount={setCount} />
))}
</div>
);
};
const Counter = ({ index, counter, setCount }) => {
return (
<div>
Counter {index} = {counter}{" "}
<button onClick={() => setCount(index, counter + 1)}>Increment</button>
</div>
);
};
render(<App />, document.getElementById("root"));
I get the index of the mapped counter, and I give it to the component so I can easily point to the correct counter in the state. The update is done with just one method that take this index as parameter.
Here is the repro on stackblitz

Categories

Resources