How can i set value in nested object in React? - reactjs

how can I set value from text Input to option[0].title when user input? I tried in many way and get error. Please help me to fix this issue.
const [options, setOptions] = useState({
0: {
title: "",
},
2: {
title: "",
1: {
title: "",
}, },
3: {
title: "",
},
title: "", });
let name, value;
const handleChange = (e) => {
name = e.target.name;
value = e.target.value;
setOptions({ ...options, [name]: value }); };
return ( <TextInput
type="text"
name="0"
value={options[0].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/> )

you just have to edit the title in the object:
const handleChange = (e) => {
name = e.target.name;
value = e.target.value;
setOptions({ ...options, [name]: { title: value } });
};
and if you need the previous information from the targeted name, spread within that object too:
setOptions({ ...options, [name]: { ...options[name], title: value } });

Fully working code,
const [options, setOptions] = useState({
0: { title: "" },
2: {
title: "",
1: { title: "" },
},
3: { title: "" },
title: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
options[+name].title = value;
setOptions({...options});
};
return (
<>
<input
type="text"
name="0"
value={options[0].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/>
<input
type="text"
name="2"
value={options[2].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/>
</>
);

I tried to check this on codesandbox and found a solution -
The name that you have updated here is string and the json has number keys. Which was the reason why it wouldn't update on title node. Also you never said .title = value, which is why values got directly updated to the options[0] element
export default function App() {
const [options, setOptions] = useState({
0: {
title: ""
},
2: {
title: "",
1: {
title: ""
}
},
3: {
title: ""
},
title: ""
});
const handleChange = (e) => {
const name = e.target.name,
value = e.target.value;
let updatedOptions = { ...options };
updatedOptions[Number(name)].title = value;
setOptions(updatedOptions);
};
return (
<div className="App">
<input
type="text"
name="0"
value={options[0].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/>
</div>
);
}

Related

"False" appearing in input after removing its text

After inserting text in input and later removing it completely with backspace "false" appears in it. It cannot be removed - after removing last letter it appears again. It appears in input which are rendered using map() method. The same problem doesn't occur in textarea which is not rendered using this method so I guess the problem lays somewhere here, but I dont have idea where.
export default function AddTodo({ active, setActive }: AddTodoProps) {
const { isDarkMode } = useContext(DarkModeContext);
const [todoDetails, setTodoDetails] = useState({
task: "",
description: "",
name: "",
deadline: "",
});
const { submitTodo, loading, status } = useAddTodoToDb(todoDetails);
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setTodoDetails((prev) => {
return {
...prev,
[e.target.id]: e.target.value || e.target.checked,
};
});
};
useEffect(() => {
if (typeof status == "string")
setTodoDetails({
task: "",
description: "",
name: "",
deadline: "",
});
}, [status]);
return (
{todoInputs.map((item) => {
return (
<StyledLabelAndInput
isDarkMode={isDarkMode}
err={status?.includes(item.id)}
key={item.id}
>
<label htmlFor={item.id}>{item.text}</label>
<input
value={todoDetails[item.id as keyof ITodoDetails]}
type={item.type}
id={item.id}
min={today}
onChange={(e) => handleChange(e)}
/>
</StyledLabelAndInput>
);
})}
<label htmlFor="description">Description (optional)</label>
<textarea
value={todoDetails.description}
id="description"
onChange={(e) =>
setTodoDetails((prev) => {
return {
...prev,
description: e.target.value,
};
})
}
/>
The false value is comming from the input's checked attribute.
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setTodoDetails((prev) => {
return {
...prev,
[e.target.id]: e.target.value || e.target.checked, /* <--- HERE */
};
});
};
You're trying to get the input's value OR checked, and since the input has no value it's returning the checked attribute, which is false by default.
Instead you'll need to check if the item type is checkbox before using the checked attribute.

React: Nested Array Form - Input field onChange handler

The form data is set by an the data object. Need help figuring out how to update /handleChange the text inputs
I've tried unique name, those probably won't work because it wont match the "key" in the object.
Any help / input is appreciated!
Data:
export default
{
name: "Restaurants Name",
menu: [
{
category: "Appetizers",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Food 2",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Food 1",
desc: "",
price: "300"
}
]
},
{
category: "Entree",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Entree 1",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Entree 1",
desc: "",
price: "300"
}
]
},
]
}
Code:
import React, { useEffect, useState } from "react";
import "./../App.css"
import MenuData from "../data"
function Edit() {
const [formState, setFormState] = useState(MenuData);
useEffect(() => {
console.log(formState)
}, []);
const handleNameChange = (event) => {
const name = event.target.name;
// console.log(name)
setFormState(prevState => ({
formState: { // object that we want to update
...prevState.formState, // keep all other key-value pairs
[name]: event.target.value, // update the value of specific key
menu: {
...prevState.menu,
items: {
...prevState.menu.items
}
}
}
}))
// setFormState({
// ...formState,
// [name]: event.target.value,
// })
};
const handleChange = (categoryIndex, event) => {
// const values = [...formState]
// values[categoryIndex][event.target.name] = event.target.value;
// setFormState(values);
const name = event.target.name;
// setFormState(prevState => ({
// formState: {
// ...prevState.formState,
// menu: {
// ...prevState.menu,
// items{
// ...prevState.items
// }
// }
// }
// }));
};
return (
<div className="App">
<div>
<input name="nameField" id="nameField" maxLength="300" value={formState.name} onChange={handleNameChange} /> <br />
{formState.menu && formState.menu.map((menuitem, categoryIndex) => {
return (
<div key={categoryIndex}>
<div class="line"></div>
<h2>{menuitem.category}</h2>
<input name={"category-" + categoryIndex} id="CategoryField" maxLength="40" categoryIndex={categoryIndex} onChange={event => handleChange(categoryIndex, event)} value={menuitem.category} />
{
menuitem.items.map((item, index) => {
return(
<div key={index}>
<input name={"title-" + index + categoryIndex} id="titleField" maxLength="40" categoryIndex={categoryIndex} onChange={handleChange} value={item.title} /> <br />
<input name="desc" id="descField" maxLength="200" categoryIndex={categoryIndex} onChange={handleChange} value={item.desc} />
<br /><br />
</div>
)
})
}
</div>
)
}
)
}
</div>
</div>
);
}
export default Edit;
UPDATED
Not able to figure out the onChange function to updated nested items

Error select based on selection from 1st select in React Hooks and dinamically inputs

I have two selects and I want to populate the second select base in the selection of the first one in react. When I select a countrie I want a select2 be displayed with its states and the value on the second select be updated with the value chose.
I have the following code,
const MyForm = (props) => {
const COUNTRIES = [
{
displayValue: "Country1",
value: "C1"
},
{
displayValue: "Country2",
value: "C2"
}
]
const STATES = {
"": [ {
displayValue: "",
value: ""
}],
"C1": [{
displayValue: "State 1",
value: "S11"
},
{
displayValue: "State 2",
value: "S12"
}],
"C2": [{
displayValue: "State n1",
value: "C21"
},
{
displayValue: "STate n2",
value: "C22"
}]
}
let inputsForms = {
country: {
elementType: 'select',
elementConfig: {
type: 'select',
placeholder: '',
options: COUNTRIES,
firstOption: "-- Choose Country"
},
value: ''
},
states: {
elementType: 'select',
elementConfig: {
type: 'select',
placeholder: '',
options: [], // I need these options depend on the countrie selected STATES["C1"]
firstOption: "-- Choose States"
},
value: ''
}
}
const [myForm, setmyForm] = useState(inputsForms);
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
const inputChangedHandler = (e, controlName) => {
const countrieValue = controlName ==="country"?e.target.value:"";
const stateOptions = myForm["states"].elementConfig;
stateOptions["options"] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value
})
});
setmyForm(updatedControls);
}
const ElementsArray = [];
for (let key in myForm) {
ElementsArray.push({
id: key,
config: myForm[key]
});
}
let form = (
<form>
{ElementsArray.map(el => (
<Input
key={el.id}
elementType={el.config.elementType}
elementConfig={el.config.elementConfig}
value={el.config.value}
changed={e => inputChangedHandler(e, el.id)}
firstOption={el.config.firstOption}
/>
))}
</form>
);
return(
<div>
{form}
</div>
);
}
export default MyForm;
The options charge on the select2, however when I select an option on the second select, the options dissappear and the value of the select2 is not updated.
Thanks.
As inputChangeHandler is getting called every input change, the data is resetting even there is change in the state. You could check for the contrieValue and set the state data so the data is not reset.
const inputChangedHandler = (e, controlName) => {
const countrieValue = controlName === "country" ? e.target.value : "";
if (countrieValue) {
const stateOptions = myForm["states"].elementConfig;
stateOptions["options"] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value
})
});
setmyForm(updatedControls);
}
}
You need to add a kind of conditional to update the state whenever the state is selected so that it does not affect the original country object.
const inputChangedHandler = (e, controlName) => {
if (countrolName === 'states') {
// your logic here
return;
}
const countrieValue = controlName === 'country' ? e.target.value : '';
const stateOptions = myForm['states'].elementConfig;
stateOptions['options'] = STATES[countrieValue];
const updatedControls = updateObject(myForm, {
[controlName]: updateObject(myForm[controlName], {
value: e.target.value,
}),
});
setmyForm(updatedControls);
};

How to cause a component to re render in react when using a hook which holds some state

I am trying to re render a component when a prop changes. My component uses a custom hook which holds the state of a form. In useEffect whenever the prop called refresh changes I want to re render the page. I have tried countless solutions including making a forceUpdate function and calling it when the prop changes, I tried changing state so that the component should re render, but the information in my for does not clear. Below is my code.
Component:
const CustomerInformationForm = (props) => {
const [triggerRefresh, setTriggerRefresh] = useState();
const initialState = {
name: {
value: "",
isValid: false,
},
phone: {
value: "",
isValid: false,
},
address: {
value: "",
isValid: true,
},
};
const initialValidity = false;
useEffect(() => {
console.log("customerinfo refreshing");
setTriggerRefresh(props.refresh);
}, [props.refresh]);
let [formState, inputHandler] = useForm(
initialState,
initialValidity
);
return (
<div>
<h3>Customer Information</h3>
<form className="customer-information_form">
<Input
id="name"
label="Name"
type="text"
element="input"
validators={[VALIDATOR_REQUIRE()]}
errorText="Please enter a valid name."
onInput={inputHandler}
onChange={props.customerInfo(formState)}
/>
<Input
type="text"
element="input"
id="phone"
label="Phone"
validators={[VALIDATOR_MINLENGTH(8)]}
errorText="Please enter a valid phone number."
onInput={inputHandler}
onChange={props.customerInfo(formState)}
/>
<Input
type="text"
element="input"
id="address"
label="Address"
validators={[]}
onInput={inputHandler}
onChange={props.customerInfo(formState)}
/>
</form>
</div>
);
};
export default CustomerInformationForm;
Custom hook:
import { useCallback, useReducer } from "react";
const formReducer = (state, action) => {
switch (action.type) {
case "INPUT_CHANGE":
let formIsValid = true;
for (const inputId in state.inputs) {
if (!state.inputs[inputId]) {
continue;
}
if (inputId === action.inputId) {
formIsValid = formIsValid && action.isValid;
} else {
formIsValid = formIsValid && state.inputs[inputId].isValid;
}
}
return {
...state,
inputs: {
...state.inputs,
[action.inputId]: { value: action.value, isValid: action.isValid },
},
isValid: formIsValid,
};
case "SET_DATA":
return {
inputs: action.inputs,
isValid: action.formIsValid,
};
default:
return state;
}
};
export const useForm = (initialInputs, initialValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialValidity,
});
const inputHandler = useCallback((id, value, isValid) => {
dispatch({
type: "INPUT_CHANGE",
value: value,
isValid: isValid,
inputId: id,
});
}, []);
const setFormData = useCallback((inputData, formValidity) => {
dispatch({
type: "SET_DATA",
inputs: inputData,
formIsValid: formValidity,
});
}, []);
return [formState, inputHandler, setFormData,];
};
Any solutions as to what I could do to get the form to re render empty??
I think the best way is to use the setFormData method returned by your hook (you have omitted it in your CustomerInformationForm component).
Then you can call it in the form, when some condition is met in an effect:
const initialState = useRef({
name: {
value: "",
isValid: false,
},
phone: {
value: "",
isValid: false,
},
address: {
value: "",
isValid: true,
},
});
let [formState, inputHandler, setFormData] = useForm(
initialState,
initialValidity
);
useEffect(() => {
console.log("customerinfo refreshing");
setTriggerRefresh(props.refresh);
setFormData(initialState.current);
}, [props.refresh]);
You can also store the initialState value with useRef, to avoid re-running the effect unnecessarily.

How to make field validation?

How to make field validation?
I have an object with fields from which I generate a form, and when submitting, I need to check each field so that it is not empty, I do this, but it doesn’t work
My form:
const [volunteerData, setVolunteerData] = useState({
fullName: {
value: '',
type: "text",
placeholder: "Name",
label: "Name"
},
phone: {
value: '',
type: "text",
placeholder: "Phone number",
label: "Phone number",
mask: "+7(999) 999 99 99"
}
)}
Render form:
const onHandleRenderForm = () => {
return Object.keys(volunteerData).map((items, idx) => {
const control = volunteerData[items];
return (
<div key={idx} className="content-data-box">
<label>{control.label}</label>
<InputMask
type={control.type}
placeholder={control.placeholder}
mask={control.mask}
onChange={e => onHandleFormData(e, items)}
/>
</div>
)
})
};
onChange input:
const onHandleFormData = (e, items) => {
const before = {...volunteerData};
const after = {...before[items]}
after.value = e.target.value;
before[items] = after;
setVolunteerData(before);
};
onClick (submit button):
const onHandleErrorBoundary = () => {
Object.keys(volunteerData).map(items => {
const errorData = items.value === '';
console.log(errorData)
})
};
Change items.value === '' to volunteerData[items].value !== ""
const onHandleErrorBoundary = () => {
Object.keys(volunteerData).map(items => {
const errorData = volunteerData[items].value !== "";
return console.log(errorData);
});
};
you can check here codesandbox

Resources