Component Rerendering On Change NextJS - reactjs

I have a simple input with an onchange function that takes the value and sets the state for whichever one I want. I have used an input onChange before in other parts of the code, but this issue has never happened before. Every time I would type in a number in the input, it deselects the input and doesn't let me input anymore. This is the code for the input including the state set;=
const [calc, setCalc] = useState("");
const [iotInitial, setIotInitial] = useState(0);
const [iotCont, setIotCont] = useState(0);
const [iotGrowth, setIotGrowth] = useState(0);
const [iotSubmit, setIotSubmit] = useState(false)
const Calculator = () => {
if (calc === "1") {
return (
<div className="text-black p-2">
<h1 className="text-lg">Investment Over Time</h1>
<div className="">
<div className="flex flex-wrap gap-x-5">
<div className="flex flex-col">
<label>Initial Investment</label>
<input defaultValue={iotInitial} value="initial" type="number" className="rounded" onChange={(e) => setIotInitial(e.target.value)}/>
</div>
<div className="flex flex-col">
<label>Contributions (monthly)</label>
<input defaultValue={iotCont} value="cont" type="number" className="rounded" onChange={(e) => setIotCont(e.target.value)}/>
</div>
<div className="flex flex-col">
<label>Growth Time (years)</label>
<input defaultValue={iotGrowth} value="growth" type="number" className="rounded" onChange={(e) => setIotGrowth(e.target.value)}/>
</div>
<button className="bg-blue-300 hover:bg-blue-500 px-5 rounded" onClick={() => {setIotSubmit(true)}}>
Submit
</button>
</div>
{iotSubmit &&
<div>
{iotInitial}
{iotCont}
{iotGrowth}
</div>
}
</div>
</div>
);
} else if (calc === "2") {
return (
<div className="text-black p-2">
<h1 className="text-lg">Risk Analysis Using Average True Range</h1>
<p>Coming Soon</p>
</div>
);
} else if (calc === "3") {
return (
<div className="text-black">
<h1 className="text-lg">Hello</h1>
<p>{calc}</p>
</div>
);
}
};
This component keep rerendering and I don't know why. Any help would be useful.

you use value="initial" which is a string and what you should do is
...
<input
defaultValue="0"
value={iotCont}
type="number"
onChange={(e) => setIotCont(e.target.value)}
/>
...
The problem is that input uses value attribute as what it is gonna display
or you may simply remove value from your input to make it one way binding

Related

Passing values from a constant to the e.target.value

I am working on building out a radio component and when checked I want the values from the radio button to pass when set onBlur. I am trying to pass a function to the onBlur but I am not seeing the values on my Form Submit action.
const [isChecked, setIsChecked] = React.useState<boolean>(defaultValue);
const onValue = React.useMemo(() => {
if (!isChecked) return [`${label}, ${description}`];
}, [onChange]);
const checkedValue = React.useCallback(
(e: React.ChangeEvent<HTMLInputElement>) =>
onChange?.(onValue, e.target.value),
[onChange]
);
return (
<div className="space-y-5">
<div className="relative flex items-start">
<div key={id} className="flex h-5 items-center">
<input
id={`${label}-id`}
name={label}
type="radio"
className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-500"
onBlur={checkedValue)}
onChange={() => setIsChecked(!isChecked)}
/>
</div>
<div className="ml-3 text-sm">
<label
htmlFor={`${label}-id`}
className="block font-medium text-gray-700"
>
{label}
</label>
<p id={`${label}-description`} className="text-gray-500">
{description}
</p>
</div>
</div>
</div>
);
};

Line 18:19: 'value' is missing in props validation

I have this register page and it keeps showing me this error "Line 18:19:'value' is missing in props validation":
const Register = () =>{
const [value, setMyValue] = useState()
This function shows up a component based on the choice of the user that happens in the select tag
const Choice = ({ value }) =>{
if (value === "true"){
return <Employee />
}else{
return <Employer />
}
}
return(
<div>
<Navbar />
<div className="container-xxl bg-primary page-header">
<div className="container text-center">
<h1 className="text-white animated zoomIn">Sign Up</h1>
<nav aria-label="breadcrumb">
</nav>
</div>
</div>
<div className="container-xxl py-5" id="contact">
<div className="container">
<div className="mx-auto text-center wow fadeInUp" data-wow-delay="0.1s" style=
{{maxWidth: 600}}>
<div className="d-inline-block border rounded-pill text-primary px-4 mb-2">Sign Up
As</div>
<select onChange={(e)=>{setMyValue(e.target.value)}} className="form-select" aria-
label="Default select example">
<option value="false">Employer</option>
<option value="true">Employee</option>
</select>
<h2 className="mb-5 mt-4">Sign Up to find the best employees</h2>
</div>
<div>
<Choice value={ value } />
</div>
</div>
</div>
</div>
)
}
And this is the line 18 by the way:
const Choice = ({ value }) =>{
Maybe the problem could be 2 brackets in line 7?
if (value === "true"){
return <Employee />
}else{
return <Employer />
}

React checkbox doesn't keep toggled (think is in infinite loop)

Basically i have this:
const [searchUser, setSearchUser] = useState<string[]>([])
Which i pass as a filter on an array:
reportsData
.filter((value: any) =>
searchUser.length > 0
? searchUser.includes(value.user.name)
: true
)
And i created checkboxes that passes values to this searchUser state so i can filter my array with one (or multiple checkboxes)
Like this:
const EmittersComponent: React.FC<PropsButton> = ({ label, onSelect }) => {
const [checked, setChecked] = useState(false)
function handleSelect() {
onSelect(label)
setChecked(!checked)
}
return (
<div className="grid grid-cols-3 gap-3 lg:grid-cols-2">
<li className="mt-4 flex items-start">
<div className="flex items-center h-5">
<input
type="checkbox"
onChange={() => {
setChecked(checked)
handleSelect()
}}
checked={checked}
className="h-4 w-4 focus:bg-indigo border-2 border-gray-300 rounded"
/>
</div>
<div className="ml-3 text-sm">
<span className="font-medium text-gray-700">
{label || 'Sem nome'}
</span>
</div>
</li>
</div>
)
}
function handleToggle(label: string) {
setSearchUser((prev) =>
prev.some((item) => item === label)
? prev.filter((item) => item !== label)
: [...prev, label]
)
}
const emittersComponent = () => (
<div>
{emittersData.map((value: any, index: any) => (
<EmittersComponent
key={index}
label={value.Attributes[2]?.Value}
onSelect={handleToggle}
/>
))}
</div>
)
Then i render it on my react component <ul>{emittersComponent()}</ul>
But the thing is, it is working everything correctly (if i select one or multiple checkboxes, it filters my array), but the checkbox won't keep toggled. It will render as if it was untoggled (the blank, unchecked box) no matter what i do.
I think is in an infinite loop and i can't fix it.
You have called setChecked in the
onChange={() => {
setChecked(checked)
handleSelect()
}}
and then setChecked is calling inside handleSelect function. That is not correct.
I assume it should be onChange={handleSelect}
You are creating a component inside another component with its own state, therefore a emittersComponent is created in every render.
Move Emitters component and emittersComponent(changed to EmittersComponentList ) function out of Quick Sight Component.
Try the below if this doesnt work then you have to have a logic to know which of the emittersData is checked.
const EmittersComponentList = ({ emittersData, handleToggle }) => (
<div>
{emittersData.map((value: any, index: any) => (
<EmittersComponent
key={value.Attributes[2]?.Value} // Dont add index as the key, add some unique val
label={value.Attributes[2]?.Value}
onSelect={handleToggle}
/>
))}
</div>
);
const EmittersComponent: React.FC<PropsButton> = ({ label, onSelect }) => {
const [checked, setChecked] = useState(false);
function handleSelect() {
onSelect(label);
setChecked(!checked);
}
return (
<div className="grid grid-cols-3 gap-3 lg:grid-cols-2">
<li className="mt-4 flex items-start">
<div className="flex items-center h-5">
<input
type="checkbox"
onChange={() => {
setChecked(checked);
handleSelect();
}}
checked={checked}
className="h-4 w-4 focus:bg-indigo border-2 border-gray-300 rounded"
/>
</div>
<div className="ml-3 text-sm">
<span className="font-medium text-gray-700">
{label || "Sem nome"}
</span>
</div>
</li>
</div>
);
}
;
In your Quick Sight Component
<div className="grid grid-cols-1 lg:grid-cols-2 lg:gap-6">
<div>
<span className="text-xl font-medium text-accent-9">
Escolha os emissores:
</span>
<ul>
{
/* Instead of emittersComponent()
use below
*/
<EmittersComponentList
emittersData={emittersData}
handleToggle={handleToggle}
/>
}
</ul>
</div>
<div>

Adding new objects multiple values to list using UseState & form ReactJS

I've been trying to create a form that can add a list of objects with multiple attributes to a list. I managed to get this right with one string attribute. But, I cannot figure out how to add an entire object with property values passed from the form. I'm using functional components to do this........How can I create a form that adds new objects of items to a list? I'm fairly new to ReactJS, btw.
resume.jsx
function App() {
const [jobExperience, setjobExperience] = useState([{
jobCompanyName: '',
jobDateRangeStart: '',
jobDateRangeEnd: '',
jobDescription: '',
reference_up_count: 0,
reference_down_count: 0,
}]);
const refUpvoteCount = index => {
const newReferences = [...jobExperience];
newReferences[index].reference_upvote_count++;
setjobExperience(newReferences)
}
const refDownvoteCount = index => {
const newReferences = [...jobExperience];
newReferences[index].reference_downvote_count++;
setjobExperience(newReferences)
}
return(
<Container className="container-fluid g-0">
<Row>
<Col>
<div>
{jobExperience.map((jobExp, index) => (
<JobExperience key={index} jobExperience={jobExp} refUpvote={refUpvoteCount} refDownvote={refDownvoteCount}
))}
</div>
</Col>
<Col>
<div className="pl-5 pr-5 pb-2">
<form onSubmit={//Add To Array of item Objects}>
<div className="form-group">
<label>Company Name</label>
<input type="text" className="form-control" placeholder="Add Company Name" name="jobCompanyName" onChange={handleJobExperienceChange} />
</div>
<div className="form-row">
<div className="col">
<div className="form-group">
<label>Start Date</label>
<Datetime dateFormat="YYYY" timeFormat={false} onChange={(date) => setstartDate(date.year())} value={jobExperience.jobDateRangeStart} />
</div>
</div>
<div className="col">
<div className="form-group">
<label>End Date</label>
<Datetime dateFormat="YYYY" name="jobDateRangeEnd" timeFormat={false} onChange={(date) => setendDate(date.year())} value={jobExperience.jobDateRangeEnd} />
</div>
</div>
</div>
<div className="pt-1">
<div className="form-group">
<label>Job Role/Responsibilities</label>
<textarea style={{width: '100%'}} name="jobDescription" onChange={handleJobExperienceChange} />
<button type="submit" onClick={handleJobExperienceAdd} className="btn btn-success btn-sm btn-block">Add Job Experience</button>
</div>
</div>
</div>
</form>
</Col>
</Row>
</Container>
)
}
function JobExperience({jobExperience, index, refUpvote, refDownvote}) {
return (
<div>
<Card style={{width: '18rem'}} className="remove-border-radius">
<Card.Body>
<Card.Title><span><i className="fa fa-building"></i> {jobExperience.jobCompanyName}</span></Card.Title>
</Card.Body>
<Card.Text>
<i className="fa fa-calendar"></i> {jobExperience.jobDateRangeStart}-{jobExperience.jobDateRangeEnd}
</Card.Text>
<Card.Text>
<span><i className="fa fa-info-circle"></i> {jobExperience.jobDescription}</span>
</Card.Text>
<Button variant="primary" onClick={() => refUpvote(index)} className="remove-border-radius"><i className="fa fa-plus"></i> Reference {jobExperience.reference_upvote_count}</Button>
<Button variant="danger" onClick={() => refDownvote(index)} className="remove-border-radius"><i className="fa fa-minus-circle"></i> Reference {jobExperience.reference_downvote_count}</Button>
</Card>
</div>
)
}
Change the way you set your state from this:
const refUpvoteCount = (index) => {
const newReferences = [...jobExperience];
newReferences[index].reference_upvote_count++;
setjobExperience(newReferences);
};
const refDownvoteCount = (index) => {
const newReferences = [...jobExperience];
newReferences[index].reference_downvote_count++;
setjobExperience(newReferences);
};
To this:
const refUpvoteCount = (index) => {
setjobExperience((previousState) => {
const newReferences = [...previousState];
newReferences[index].reference_upvote_count++;
return newReferences;
});
}
const refDownvoteCount = (index) => {
setjobExperience((previousState) => {
const newReferences = [...previousState];
newReferences[index].reference_downvote_count++;
return newReferences;
});
}
You may also take note the difference to understand this other way of setting-up state that needs to have the the value of the previous state
Do it like this.
const myFunction = () => {
setState((previousState)=> newState)
}
If you need to get the reference of the previous state pass a callback function on setState and that call back function can take 1 parameter which that represent the previous state. And on the callback function you can do some operations if you need to. The return value of callback function will be the new state
And not like this
const myFunction = () => {
const newState = state
setState(newState)
}
This last code sample reference the previous state the wrong way and will not work
const [form, setForm] = useState({}); // form is the previous jobExperience object
const onChange = (event) => {
const { name, value } = event.target;
let savedValue = value;
/*
condition your changes below, you can also extract
the content of the condition to separate functions
*/
if (name === 'jobDateRangeStart') {
savedValue = []; // whatever you need to do with the value
}
if (name === 'jobDateRangeEnd') {
savedValue = []; // whatever you need to do with the value
}
if (name === 'jobDateRangeEnd') {
savedValue = []; // whatever you need to do with the value
}
setForm({ ...form, [name]: savedValue });
};
return (
<div className="pl-5 pr-5 pb-2">
<div className="form-group">
<label>Company Name</label>
<input
className="form-control"
name="jobCompanyName"
onChange={handleChange}
placeholder="Add Company Name"
type="text"
value={form.jobCompanyName || ''}
/>
</div>
<div className="form-row">
<div className="col">
<div className="form-group">
<label>Start Date</label>
<Datetime
dateFormat="YYYY"
onChange={handleChange}
timeFormat={false}
value={form.jobDateRangeStart || ''}
/>
</div>
</div>
<div className="col">
<div className="form-group">
<label>End Date</label>
<Datetime
dateFormat="YYYY"
name="jobDateRangeEnd"
onChange={handleChange}
timeFormat={false}
value={form.jobDateRangeEnd || ''}
/>
</div>
</div>
</div>
<div className="pt-1">
<div className="form-group">
<label>Job Role/Responsibilities</label>
<textarea
name="jobDescription"
onChange={handleChange}
value={form.jobDescription || ''}
style={{width: '100%'}}
/>
<button
className="btn btn-success btn-sm btn-block"
onClick={handleChange}
type="submit"
>
Add Job Experience
</button>
</div>
</div>
</div>
);
As far as i understood you are trying to add an object into an array with multiple fields . and the value of object will come from the values of your form . Here's how can you do it.
# Step 1 :
first create a state that will hold the array of objects .
const [arrayOfObjects , setArrayOfObjects ] = useState([]) ; // empty array initially
# Step 2 :
grab the value from your form's submit function and create the object
onSubmitHandler = () => {
const newObject = {
property1 : "some value " // this values will come from your form
property2 : "some value " // depending on your implementation you may have to maintain separate state for each property
}
const newState = [ ...arrayOfObjects , newObject ];
setArrayOfObjects(newState);
}

How to open one dropdown item?

friends, I have array of questions, and a dropdown list for them... i want to open any question, but all questions are opening together... please help
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState(false)
const toggle = (id) => {
questions.forEach((q) => {
if(q.id === id){
setIsOpenAnswer((prevState) => !prevState)
}
})
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
Use a Javascript object to track which unique q.id is being set to true.
const FAQ = () => {
const [isOpenAnswer, setIsOpenAnswer] = useState({})
const toggle = (id) => {
setIsOpenAnswer(prevState => ({
...prevState,
[id]: !prevState[id],
});
}
return <Layout>
<div className="questionsBox pb-5">
<h2 className="title pt-4 pb-4" >Frequently Asked Questions</h2>
{questions.map((q, index) => {
return <div className="question pl-1 pt-3 pb-3 pr-1" key={index}>
<div className="d-flex justify-content-between">
<span className="questionTitle">{q.question}</span>
<img className="questionIcon"
src={Plus} alt="plus"
onClick={() => toggle(q.id)}
/>
</div>
{isOpenAnswer[q.id] && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
</div>
})}
</div>
</Layout>
}
You're using the same prop for all of them here:
{isOpenAnswer && <p className="answer pt-2 pb-2">
{q.answer}
{q.source}
</p>}
Try saving something unique in state to identify what you're supposed to be showing, e.g.,
{selectedQuestionId && /* the rest */ }
and set the selectedQuestionId where you're currently setting isOpenAnswer .

Resources