Add object inside of array react input value - javascript

I want add object inside of array items
I am trying to manage objects inside of an array with useState but is not working i have only in object but I want the object in interior of the array of items. When I click add items on the button i want add the element and if possible remove this element when i click remove items in button link with inputs (see the image)
Like :
company:"Apple",
companyAdress:"5 avenue triagle",
items: [
{
itemName: Computer,
itemQuantity: 20,
itemPrice: 209
},
{
itemName: Computer,
itemQuantity: 20,
itemPrice: 209
},
]
My code :
const [info, setInfo] = useState({});
const [itemForm, setItemForm] = useState({ num: 1 })
const handleRemoveItem = (e) => {
e.preventDefault();
setItemForm((itemForm) => ({ num: itemForm.num - 1 }))
}
const handleAddItem = (e) => {
e.preventDefault();
setItemForm((itemForm) => ({ num: itemForm.num + 1 }))
}
<label>Company</label>
<input onChange={(e) => { setInfo({ ...info, company: e.currentTarget.value}); }} placeholder="Company"></input>
<label>company Adress</label>
<input onChange={(e) => { setInfo({ ...info, companyAdress: e.currentTarget.value }); }} placeholder="Adresse"></input>
<ul className="space-y-3">
{[...Array(itemForm.num)].map((x, i) => {
return (
<li key={i}>
<div>
<input onChange={(e) => { setInfo({...info,itemName: e.currentTarget.value });}} name="itemName" placeholder="itemName:" ></input>
<input onChange={(e) => { setInfo({ ...info, itemQuantity: e.currentTarget.value }); }} type="number" name="itemQuantity" placeholder="Quantity:"></input>
<input onChange={(e) => { setInfo({ ...info, itemPrice: e.currentTarget.value }); }} type="number" name="itemPrice" placeholder="Price:"></input>
<button onClick={handleRemoveItem}>Enlever </button>
<button onClick={handleAddItem}>+ Add New Item</button>
</div>
</li>
)
}
)}
</ul>

i do something like this using an id to find the iteminfo.
i am currying the itemid here but you could put the itemId as part of the input id and find it that way if you like - then you could use one function. anyway hope it helps
also i am just using the id as the key for the object you might what to be more strict on this ¯_(ツ)_/¯
i would also put the factory and defaultInfo else where in your app
import { useState } from "react";
import { v4 as uuidv4 } from "uuid";
const defaultItemFactory = () => {
return { itemName: "", itemQuantity: "", itemPrice: "", id: uuidv4() };
};
const defaultInfo = {
company: "",
companyAdress: "",
items: [defaultItemFactory()],
};
function App() {
const [info, setInfo] = useState(defaultInfo);
const changeHanlder = (event) => {
const { id, value } = event.currentTarget;
setInfo((_info) => {
return { ..._info, [id]: value };
});
};
const itemHanlder = (itemId) => (event) => {
const { id, value } = event.currentTarget;
setInfo((_info) => {
if (id === "add")
return { ..._info, items: _info.items.concat(defaultItemFactory()) };
const items = _info.items
.map((item) => {
if (item.id !== itemId) return item;
if (id === "remove") return null;
return { ...item, [id]: value };
})
.filter((out) => out);
return { ..._info, items };
});
};
return (
<div className="App">
<label>Company</label>
<input
id={"company"}
value={info.company}
onChange={changeHanlder}
placeholder="Company"
></input>
<label>company Adress</label>
<input
id={"companyAdress"}
value={info.companyAdress}
onChange={changeHanlder}
placeholder="Adresse"
></input>
<ul className="space-y-3">
{info.items &&
info.items.map((item, i) => {
return (
<li key={`item-${item.id}`}>
<div>
<input
id={"itemName"}
value={item.itemName}
onChange={itemHanlder(item.id)}
name="itemName"
placeholder="itemName:"
></input>
<input
id={"itemQuantity"}
value={item.itemQuantity}
onChange={itemHanlder(item.id)}
type="number"
name="itemQuantity"
placeholder="Quantity:"
></input>
<input
id={"itemPrice"}
value={item.itemPrice}
onChange={itemHanlder(item.id)}
type="number"
name="itemPrice"
placeholder="Price:"
></input>
<button id={"remove"} onClick={itemHanlder(item.id)}>
Enlever{" "}
</button>
<button id={"add"} onClick={itemHanlder(item.id)}>
+ Add New Item
</button>
</div>
</li>
);
})}
</ul>
</div>
);
}
export default App;

Related

React.js inserting multiple states into one main state

I'm trying to make an app where an 'Owner' can have multiple 'cars', I have my App.js file where the Owner enters there name and can enter there car details ('Car name' and 'Car Type'), a Owner can have multiple Cars, and when they click 'Add car entry' a Component where they enter there car details called 'OwnersCars' is repeated. Like so
to
If an owner fills out the input boxes in this component (For X amount of cars) then clicks 'Save Owner' i want the owner aswell as a list of all there cars to be saved into one State.
Currently i have my app.js file like this (count is used to know the number of OwnersCars divs)
import './App.css';
import React, {useState, useRef} from 'react';
import OwnersCars from './ownersCars';
function App() {
const [count, setCount] = useState(1)
const [OwnerInput, SetOwnerInput] = useState({
id: "",
Name: "",
cars: []
});
const [newCarInput, SetnewCarInput] = useState({
id: "",
Type: "",
CarName: ""
});
const removeDiv = () => {
//console.log('sw\nag')
setCount(count - 1)
}
const repeatDiv = () => {
//console.log('sw\nag')
setCount(count + 1)
}
const displayCarInput = (e) => {
//console.log(count, "<--key")
return ( ([...Array(count)].map((e, i) => <OwnersCars onAddNameCar={addNewCarNameHandler} onAddTypeCar={addNewCarTypeHandler}></OwnersCars> )))
}
const displayRemove = (e) =>{
if (count > 1) {
return (<button className='removeAnimalButton' onClick={removeDiv}> <dt> Remove Last Animal Entry</dt></button>)
}
}
const NameHandler = (e) => {
//console.log(e.target.value)
SetOwnerInput((prevState) => {
return { ...prevState, Name: e.target.value };
});
}
const submitHandler = (event) => {
event.preventDefault();
const value = Math.random().toString()
const OwnerData = {
id: value,
Name: OwnerInput.Name,
cars: [newCarInput]
};
console.log(OwnerData, "<--- ownerdata with cars data");
}
const addNewCarNameHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, CarName: values };
});
};
const addNewCarTypeHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, Type: values };
});
};
return (
<div>
<div>
<div>
<label for="exampleInputPassword1"></label>
<button onClick={submitHandler} ><dt>Save Owner</dt></button>
</div>
</div>
<hr/>
<div className="wrapper">
<div class="new-owner-div">
<h5>Owner</h5>
<hr/>
<form>
<div>
<input type="name" id="exampleInputClinic" placeholder="Owner Name" onChange={NameHandler}/>
</div>
</form>
</div>
<div class="new-owner-div-2">
<h5>Owners Cars</h5>
<hr/>
{displayCarInput()}
<div>
<button onClick={repeatDiv}> <dt> Add Car Entry</dt></button>
{displayRemove()}
</div>
</div>
</div>
</div>
);
}
export default App;
and i have my ownersCars.js file with the OwnersCars component like this
import React, {useState, useRef} from 'react';
function OwnersCars(props) {
const CarNameHandler = (e) => {
console.log(e.target.value)
props.onAddNameCar(e.target.value)
}
const CarTypeHandler = (e) => {
console.log(e.target.value)
props.onAddTypeCar(e.target.value)
}
return(
<div>
<div>
<div>
<h3>Car name</h3>
<span></span>
<h3>Type</h3>
</div>
<div>
<div>
<input placeholder="Car Name" onChange={CarNameHandler}/>
</div>
<span class="span1-box"></span>
<div class="height">
<input class="input-box-OA-2" placeholder="Car Type" onChange={CarTypeHandler}/>
</div>
<span class="span1-box"></span>
</div>
</div>
</div>
)
}
export default OwnersCars
but when i click save user it only saves the latest car entry!
Would anyone be able to help?
Sorry for the mess and lack of css i removed a bunch of things from the original code so it was easier to follow on StackOverflow. Also im fairly new to react so im sure theres alot of things that need to be changed for this to work.
You need to push to owner cars, every time you add a new car. Please find the code below for App.js changes. check repeatDiv. Similarly, you need to pop from cars the particular car with remove div which I leave it to you
import React, { useState, useRef } from "react";
import OwnersCars from "./Owner";
function App() {
const [count, setCount] = useState(1);
const [OwnerInput, SetOwnerInput] = useState({
id: "",
Name: "",
cars: []
});
const [newCarInput, SetnewCarInput] = useState({
id: "",
Type: "",
CarName: ""
});
const removeDiv = () => {
//console.log('sw\nag')
setCount(count - 1);
};
const repeatDiv = () => {
//console.log('sw\nag')
OwnerInput.cars.push(newCarInput);
setCount(count + 1);
};
const displayCarInput = (e) => {
//console.log(count, "<--key")
return [...Array(count)].map((e, i) => (
<OwnersCars
onAddNameCar={addNewCarNameHandler}
onAddTypeCar={addNewCarTypeHandler}
></OwnersCars>
));
};
const displayRemove = (e) => {
if (count > 1) {
return (
<button className="removeAnimalButton" onClick={removeDiv}>
{" "}
<dt> Remove Last Animal Entry</dt>
</button>
);
}
};
const NameHandler = (e) => {
//console.log(e.target.value)
SetOwnerInput((prevState) => {
return { ...prevState, Name: e.target.value };
});
};
const submitHandler = (event) => {
event.preventDefault();
const value = Math.random().toString();
const OwnerData = {
id: value,
Name: OwnerInput.Name,
cars: OwnerInput.cars
};
console.log(OwnerData, "<--- ownerdata with cars data");
};
const addNewCarNameHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, CarName: values };
});
};
const addNewCarTypeHandler = (values) => {
//console.log(values, "<---5")
SetnewCarInput((prevState) => {
return { ...prevState, Type: values };
});
};
return (
<div>
<div>
<div>
<label for="exampleInputPassword1"></label>
<button onClick={submitHandler}>
<dt>Save Owner</dt>
</button>
</div>
</div>
<hr />
<div className="wrapper">
<div class="new-owner-div">
<h5>Owner</h5>
<hr />
<form>
<div>
<input
type="name"
id="exampleInputClinic"
placeholder="Owner Name"
onChange={NameHandler}
/>
</div>
</form>
</div>
<div class="new-owner-div-2">
<h5>Owners Cars</h5>
<hr />
{displayCarInput()}
<div>
<button onClick={repeatDiv}>
{" "}
<dt> Add Car Entry</dt>
</button>
{displayRemove()}
</div>
</div>
</div>
</div>
);
}
export default App;
And the output with cars saved

Check if all checkboxes are checked and console.log("all checked") Javascript/React

The following component is meant to be a To-Do list. I got multiple checkboxes. As soon as all checkboxes are checked, a console.log("all checked") should appear.
My idea is to check if the todo.lengh === checked.length, but it doesn't work.
Problem: trying to console.log(checked.length) doesn't work either so there must be the problem.
Can someone help me how to reach the checked.length?
import React from 'react';
import { AiOutlinePlusCircle } from 'react-icons/ai';
import { useState } from 'react';
function Checkboxes() {
const [todo, setToDo] = React.useState('');
const [todos, setToDos] = React.useState([]);
const [checked, setChecked] = useState(false);
function handleToDoSubmit(e) {
e.preventDefault();
const newToDo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setToDos([...todos].concat(newToDo));
setToDo('');
}
function toggleCompleteToDo(id) {
const updatedToDos = [...todos].map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
setToDos(updatedToDos);
}
function allChecked(checked) {
if (todo.length === checked.length) {
console.log('all checked');
}
}
return (
<div className="ToDoList">
<form className="goalInputToDo" onSubmit={handleToDoSubmit}>
<input
className="goalInput"
type="text"
onChange={(e) => setToDo(e.target.value)}
value={todo}
/>
<button className="AddGoalBtn" type="submit">
.
<AiOutlinePlusCircle size="2em" />
</button>
</form>
{todos.map((todo) => (
<div className="goalItem">
<div key={todo.id}>
<div>{todo.text}</div>
<input
type="checkbox"
onChange={() => {
toggleCompleteToDo(todo.id), allChecked(todo.checked);
}}
checked={todo.completed}
/>
</div>
</div>
))}
</div>
);
}
export default Checkboxes;
todo is a string, checked is a boolean, so I'm not sure how you wanted to use them to check if all the checkboxes are checked. What you could do instead is to check your todos array and check if every single item's completed prop is true.
You can use Array#every() to do this. It tests whether all elements in the array pass the test implemented by the provided function:
function allChecked() {
return todos.every(item => item.completed)
}
function App() {
const [todo, setToDo] = React.useState('');
const [todos, setToDos] = React.useState([{
id: new Date().getTime(),
text: 'First item',
completed: false,
}]);
function handleToDoSubmit(e) {
e.preventDefault();
const newToDo = {
id: new Date().getTime(),
text: todo,
completed: false,
};
setToDos(todos.concat(newToDo));
setToDo('');
}
function toggleCompleteToDo(id) {
const updatedToDos = todos.map((item) => {
if (item.id === id) {
item.completed = !item.completed;
}
return item;
});
setToDos(updatedToDos);
}
function allChecked() {
if (!todos.length) return false;
return todos.every((item) => item.completed);
}
return (
<div className="ToDoList">
<form className="goalInputToDo" onSubmit={handleToDoSubmit}>
<input
className="goalInput"
type="text"
onChange={(e) => setToDo(e.target.value)}
value={todo}
/>
<button className="AddGoalBtn" type="submit">
Add
</button>
</form>
{todos.map((item) => (
<div className="goalItem">
<div key={item.id}>
<input
type="checkbox"
onChange={() => {
toggleCompleteToDo(item.id), allChecked(item.checked);
}}
checked={item.completed}
/>
<span>{item.text}</span>
</div>
</div>
))}
<p>All checked: {allChecked() ? 'Yes' : 'No'}</p>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>

Delete mapped element by click React

In my state I have an array, which I mapped in render function:
State
this.state = {
newItem: {
name: "",
amount: 0
},
products: [
{
name: "Item",
amount: 5
},
{
name: "Item",
amount: 5
},
{
name: "Item",
amount: 5
}
]
}
I want to delete element from the array products by clicking in button of that element. But what I attempted to do, everything fails. I searched a lot and find logically the most common solution but it didn't work too. Here is the delete function:
Delete function
delete(e) {
this.setState(prevState => ({ products: prevState.products.filter((product) => {
return product !== e.target.value
})}))
console.table(this.state.products)
}
Mapped JSX code
{
this.state.products.map((item, index) => {
return(
<div key={index}>
<input readOnly value={this.state.products[index].name} type="text" />
<button disabled>-</button>
<input readOnly value={this.state.products[index].amount} type="number" />
<button disabled>+</button>
<button onClick={(e) => this.delete(e)}>Delete</button>
</div>
)
})
}
You need to pass index of that element into delete which will be removed like below.
<button onClick={() => delete(index)}>Delete</button>
Then you can remove that element easily.
delete = index => {
this.setState(prevState =>
({
products: prevState.products.filter((product, i) => i !== index)
})
);
}
Note that setState is asynchronous, so using console.log(this.state.products) immediately after calling setState will not print the updated products. You can pass a callback in the second parameter of setState, it will be executed once setState is completed.
The other problem comes from the delete function. You are using e.target.value which is an empty string because the button has no value.
You can fix this with the following code :
delete(product) {
this.setState(prevState => ({
products: prevState.products.filter(p => p !== product)
}),
() => console.log(this.state.products));
}
In the JSX code, I pass the product in the first argument of the delete function :
{
this.state.products.map((product, index) => (
<div key={index}>
<input readOnly value={product.name} type="text"/>
<button disabled>-</button>
<input readOnly value={product.amount} type="number"/>
<button disabled>+</button>
<button onClick={() => this.delete(product)}>Delete</button>
</div>
))
}
A simplified example of how to make item specific buttons on a mapped list:
delete = (id) => {
this.setState(products: this.state.products.filter((item, index) => index !== idx));
}
here is the solution and working example here https://codesandbox.io/s/dawn-tdd-js8yz
state = {
newItem: {
name: "",
amount: 0
},
products: [
{
name: "Item1",
amount: 5
},
{
name: "Item2",
amount: 5
},
{
name: "Item3",
amount: 5
}
]
}
delete(e: any) {
var products = this.state.products.filter((product) => {
return product.name !== e.name
});
console.log(products)
this.setState({products: products})
console.table(this.state.products)
}
render() {
const { products } = this.state;
return (
<>
{products.map((x, index) => {
return <div key={index}>
<input readOnly value={this.state.products[index].name} type="text" />
<button disabled>-</button>
<input readOnly value={this.state.products[index].amount} type="number" />
<button disabled>+</button>
<button onClick={this.delete.bind(this, x)}>Delete</button>
</div>
})}
</>
)
}

React - checkbox values are undefined despite having a default value

I have a bit of an issue that’s causing size and maxArrayElements on all checkboxes selected after the first checkbox to be undefined if the input boxes are untouched. They are all set to default to 1 if not touched.
So in the sandbox, pick a dataschema, selectorField, check one box, just choose the lengthType, and hit submit. The object will come back (in the console) with the default values for size and arrayElements of 1.
Now if I check another box, just choose the lengthType, and hit submit, the size and arrayElements values come back undefined if not touched. They should default to 1. I do have a placeholder value set to 1, so it might be misleading. Specifically, the value prop on the inputs value={this.state.size[lastCheckedFieldName] || 1} handle this by setting the default value to 1 if not changed. But for some reason this isn't happening
This sandbox is reproducing the issue.
import React from "react";
import ReactDOM from "react-dom";
import { Checkbox, CheckboxGroup } from "react-checkbox-group";
import axios from "axios";
import "./styles.css";
const schemas = [{ name: "Phone Data", id: "1" }];
const data = {
data: {
id: "2147483601",
name: "Phone Data",
fields: [
{
name: "Callee #"
},
{
name: "Caller #"
},
{
name: "Duration"
}
]
}
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
schemas: [],
fields: [],
size: {},
lengthType: {},
maxArrayElements: {},
fieldNames: [],
submitDisabled: true
};
this.onDataSchemaChange = this.onDataSchemaChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
componentDidMount() {
axios({
method: "get",
url: `/list/of/schemas`
})
.then(response => {
console.log(response);
this.setState({ schemas: schemas });
})
.catch(error => console.log(error.response));
}
onDataSchemaChange = event => {
const schema = this.state.schemas.find(
schema => schema.name === event.target.value
);
if (schema) {
axios({
method: "get",
url: ``
})
.then(response => {
console.log(response);
this.setState({
fields: data.data.fields,
selectedId: data.data.id
});
console.log(this.state.selectedId);
console.log(this.state.fields);
})
.catch(error => console.log(error.response));
}
};
onSizeChange = e => {
e.persist();
const { fieldNames } = this.state;
const lastCheckedFieldName = fieldNames[fieldNames.length - 1];
this.setState(
prevState => {
return {
size: {
...prevState.size,
[lastCheckedFieldName]: e.target.value
}
};
},
() => {
console.log(this.state.size);
}
);
console.log([e.target.name]);
};
onChangeMaxArrayElements = e => {
e.persist();
const { fieldNames } = this.state;
const lastCheckedFieldName = fieldNames[fieldNames.length - 1];
this.setState(
prevState => {
return {
maxArrayElements: {
...prevState.maxArrayElements,
[lastCheckedFieldName]: e.target.value
}
};
},
() => {
console.log(this.state.maxArrayElements);
}
);
console.log([e.target.name]);
};
handleSelectorFieldChange = event => {
this.setState({ selectorField: event.target.value });
};
handleCancel = event => {
event.target.reset();
};
fieldNamesChanged = newFieldNames => {
this.setState({
submitDisabled: !newFieldNames.length,
fieldNames: newFieldNames,
size: {
[newFieldNames]: 1,
...this.state.size
},
maxArrayElements: {
[newFieldNames]: 1,
...this.state.maxArrayElements
}
});
console.log(this.state.size);
console.log(this.state.maxArrayElements);
};
updateSelectorField = e => {
this.setState({ selectorField: e.target.value });
};
updateLengthType = e => {
e.persist();
const { fieldNames } = this.state;
const lastCheckedFieldName = fieldNames[fieldNames.length - 1];
console.log("e", e);
this.setState(prevState => {
const lengthType = { ...prevState.lengthType };
lengthType[lastCheckedFieldName] = e.target.value;
return {
lengthType
};
});
};
handleSubmit = event => {
event.preventDefault();
const fields = this.state.fieldNames.map(fieldName => ({
name: fieldName,
lengthType: this.state.lengthType[fieldName],
size: this.state.size[fieldName],
maxArrayElements: this.state.maxArrayElements[fieldName]
}));
console.log(fields);
};
render() {
const { schemas, fields, fieldNames, selectorField } = this.state;
const lastCheckedFieldName = fieldNames[fieldNames.length - 1];
return (
<div>
<form onSubmit={this.handleSubmit}>
<fieldset>
<legend>
<h2>QuerySchema Information</h2>
</legend>
<div className="info-boxes">
<div>
<label> Pick the dataschema to describe your data file:</label>{" "}
<select
name="schemaName"
value={this.state.value}
onChange={this.onDataSchemaChange}
>
<option value="">Choose Dataschema ...</option>
{schemas &&
schemas.length > 0 &&
schemas.map(schema => {
return <option value={schema.name}>{schema.name}</option>;
})}
</select>
</div>
</div>
</fieldset>
<fieldset>
<legend>
<h2> DataSchema Fields </h2>
</legend>
<div className="selectorField-div">
<div>
<label>Selector Field:</label>{" "}
<select
value={this.state.selectorField}
onChange={this.updateSelectorField}
required
>
<option value="">Pick Selector Field...</option>
{fields &&
fields.map(field => {
return <option value={field.name}>{field.name}</option>;
})}
</select>
</div>
{selectorField && (
<fieldset>
<div>
<legend>Choose field names</legend>
<CheckboxGroup
checkboxDepth={5}
name="fieldNames"
value={this.state.fieldNames}
onChange={this.fieldNamesChanged}
required
>
{fields &&
fields.map(field => {
return (
<li>
<Checkbox value={field.name} />
{field.name}
</li>
);
})}
</CheckboxGroup>
</div>{" "}
</fieldset>
)}
</div>
<div className="input-boxes">
{lastCheckedFieldName && (
<div>
<label>Length Type:</label>
<select
value={this.state.lengthType[lastCheckedFieldName] || ""}
onChange={this.updateLengthType}
required
>
<option value="">Select Length Type...</option>
<option value="fixed">Fixed</option>
<option value="variable">Variable</option>
</select>
</div>
)}
{lastCheckedFieldName && (
<div>
<label>Size:</label>
<input
value={this.state.size[lastCheckedFieldName] || 1}
onChange={this.onSizeChange}
type="number"
name="size"
min="1"
placeholder="1"
required
/>
</div>
)}
{lastCheckedFieldName && (
<div>
<label>MaxArray Elements:</label>
<input
value={
this.state.maxArrayElements[lastCheckedFieldName] || 1
}
onChange={this.onChangeMaxArrayElements}
type="number"
name="maxArrayElements"
placeholder="1"
min="1"
max="100"
required
/>
</div>
)}
</div>
</fieldset>
<div className="btn-group">
<span className="input-group-btn">
<button
className="btnSubmit"
handleSubmit={this.handleSubmit}
type="submit"
disabled={this.state.submitDisabled}
>
Submit{" "}
</button>
<button
className="btnReset"
handleCancel={this.handleCancel}
type="reset"
onClick={() => {
alert("Clearing current field values.");
}}
>
Reset
</button>
</span>
</div>
</form>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I have checked the output of the state at the end of your handleSubmit function, and it looks like this:
lengthType: {
"Callee #": "fixed",
"Caller #": "variable"
},
maxArrayElements: {
"Callee #,Caller #": 1,
"Callee #": 1
}
fieldNames: [
0: "Callee #"
1: "Caller #"
]
The issue here is that, the keys in both lengthType and maxArrayElements are named incorrectly. You are setting these key-value pairs onChange of the CheckboxGroup. The root of the issue is in the parameters passed to fieldNamesChanged function. CheckboxGroup always pass the Array of checked checkboxes, not just the new one. I would suggest to get only the last record from newFieldNames and add it to state.
fieldNamesChanged = newFieldNames => {
this.setState({
submitDisabled: !newFieldNames.length,
fieldNames: newFieldNames,
size: {
[newFieldNames[newFieldNames.length - 1]]: 1,
...this.state.size
},
maxArrayElements: {
[newFieldNames[newFieldNames.length - 1]]: 1,
...this.state.maxArrayElements
}
});
};

can't get the function transferred via props in React

I have the component ExpenseForm
class ExpenseForm extends Component {
state = {
description: '',
amount: '',
note: '',
createdAt: moment(),
calendarFocused: false,
error: ''
};
onInputChange = (e) => {
const prop = e.target.name;
const val = e.target.value;
if(prop === 'amount') {
if(!val || val.match(/^\d{1,}(\.\d{0,2})?$/)) {
this.setState(() => ({ [prop]:val }));
}
} else {
this.setState(() => ({ [prop]:val }));
}
};
onDateChange = (createdAt) => {
if(createdAt) {
this.setState(() => ({createdAt}));
}
};
onFocusChange = ({focused}) => {
this.setState(() => ({calendarFocused: focused}))
};
onFormSubmit = (e) => {
e.preventDefault();
const { description, amount, note, createdAt } = this.state;
if(!description || !amount) {
this.setState(() => ({error: 'Please provide description and amount'}));
} else {
this.setState(() => ({error: ''}));
console.log(this.props.onSubmit) //<<< here i get undefined
this.props.onSubmit({
description,
amount: parseFloat(amount, 10) * 100,
createdAt: createdAt.valueOf(),
note
});
}
};
render() {
console.log(this.props) //<<< here I see the object with the prop onSubmit, where lies the function onEditSubmit
return (
<div>
<h1>Expense Form</h1>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit={this.onFormSubmit}>
<input
onChange={this.onInputChange}
value={this.state.description}
name="description"
type="text"
placeholder="Description"
autoFocus
/>
<input
onChange={this.onInputChange}
value={this.state.amount}
name="amount"
type="text"
placeholder="Amount" />
<SingleDatePicker
date={this.state.createdAt}
onDateChange={this.onDateChange}
focused={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
numberOfMonths={1}
isOutsideRange={() => false }
/>
<textarea
onChange={this.onInputChange}
value={this.state.note}
name="note"
placeholder="Add a note for your expense (optional)"
></textarea>
<button>Add Expense</button>
</form>
</div>
)
}
}
I use this component in two places
The first one is here
function AddExpensePage({ addExpense, history }) {
const onAddSubmit = (data) => {
addExpense(data);
history.push('/');
};
return (
<div>
<h1>AddExpensePage</h1>
<ExpenseForm
onSubmit={onAddSubmit}
/>
</div>
)
}
And the second one is here
function EditPage(props) {
const onEditSubmit = () => {
console.log('edit submit')
};
return (
<div>
<h1>Edit Expense Page {props.match.params.id}</h1>
<ExpenseForm
onSumbit={onEditSubmit}
/>
</div>
)
}
In the first case everything works fine and I invoke the function transferred via props (onAddSubmit).
In the second one I get the error _this.props.onSubmit is not a function.
When I console.log the props of ExpenseForm I see in the object the function I transferred (onEditSubmit). But when I make console.log before calling this.props.onSubmit I see undefined in my console.
So I can't understand why this's happening.
I think that is just a typo:
onSumbit={onEditSubmit}
Should be
onSubmit={onEditSubmit}

Categories

Resources