React - checkbox values are undefined despite having a default value - javascript

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
}
});
};

Related

Add object inside of array react input value

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;

React - render class component when checkbox is selected

I have a select dropdown that when selected renders a checkbox group using <FieldArray> from formik
<FieldArray
name="fields"
render={arrayHelpers => (
<div>
{fields.map(field => (
<div key={field.name}>
<label>
<input
name="fields"
type="checkbox"
value={field.name}
onChange={e => {
if (e.target.checked) arrayHelpers.push(field.name);
else {
const idx = fields.indexOf(field.name);
arrayHelpers.remove(idx);
}
}}
/>{" "}
{field.name}
</label>
</div>
))}
</div>
)}
/>
So in the onChange method I need for each checkbox that is selected to render a class component that has additional input fields that are tied to the field name. For example, the size and length options need to chosen for each checkbox that is selected.
class FieldInputs extends React.Component {
constructor(props) {
super(props);
this.state = {
lengthType: "",
size: [],
};
this.lengthTypeChange = this.lengthTypeChange.bind(this);
this.onSizeChange = this.onSizeChange.bind(this);
}
lengthTypeChange = lengthType => {
//handle change method for lengthType
this.setState({ lengthType });
console.log("LengthType selected: ", lengthType);
};
onSizeChange = e => {
this.setState({ [e.target.name]: e.target.value });
console.log([e.target.value]);
};
render() {
return (
<div>
<h2>
{" "}
These are the input fields for each field name checkbox selected.{" "}
</h2>
<div>
<Select
id="color"
options={lengthTypeOptions}
isMulti={false}
value={lengthType}
onChange={this.lengthTypeChange}
onBlur={this.handleBlur}
placeholder={"Select a lengthType..."}
/>
</div>
<div>
<label>Size:</label>
<input
value={this.state.size}
onChange={this.onSizeChange}
type="number"
name="size"
min="1"
placeholder="1"
required
/>
</div>
</div>
);
}
}
For instance each field.name is rendered to a checkbox and should have a select dropdown for lengthType and input for size like so:
{
field.name: {
size: 1,
lengthType: "Fixed"
}
}
So I have the component designed, just need to render it to each checkbox that is selected.
How can I pass the FieldInput component to the checkboxes based on if they are toggled or not?
Inside the <Myselect> component, you can find the <FieldArray> component, using the onChange method
onChange={e => {
if (e.target.checked) {
arrayHelpers.push(field.name);
When's it's checked it also needs to render <FieldInputs>
Here's a link to a sandbox with the classes/components described above
I made many changes this is the full code
import "./helper.css";
import { MoreResources, DisplayFormikState } from "./helper";
import React from "react";
import { render } from "react-dom";
import { Formik, FieldArray } from "formik";
import * as Yup from "yup";
import axios from "axios";
import Select from "react-select";
var MockAdapter = require("axios-mock-adapter");
var mock = new MockAdapter(axios);
mock.onGet("/dataschemas").reply(200, {
data: [
{
id: "2147483602",
selfUri: "/dataschemas/2147483602",
name: "Phone Data"
}
]
});
mock.onGet("/dataschemas/2147483602").reply(200, {
data: {
id: "2147483602",
selfUri: "/dataschemas/2147483602",
type: "DataSchema",
name: "Phone Record",
fields: [
{
name: "action"
},
{
name: "callee"
},
{
name: "caller"
},
{
name: "duration"
},
{
name: "message"
},
{
name: "time_stamp"
}
]
}
});
const lengthTypeOptions = [
{ value: "fixed", label: "Fixed" },
{ value: "variable", label: "Variable" }
];
class FieldInputs extends React.Component {
constructor(props) {
super(props);
this.state = {
lengthType: "",
size: []
};
this.lengthTypeChange = this.lengthTypeChange.bind(this);
this.onSizeChange = this.onSizeChange.bind(this);
}
lengthTypeChange = lengthType => {
//handle change method for lengthType
this.setState({ lengthType }, () => {
this.props.update(this.props.name, this.state);
});
//console.log("LengthType selected: ", lengthType);
};
onSizeChange = e => {
this.setState({ [e.target.name]: e.target.value }, () => {
this.props.update(this.props.name, this.state);
});
//console.log([e.target.value]);
};
render() {
const { lengthType } = this.state;
return (
<div>
<h2>
{" "}
These are the input fields for each field name checkbox selected.{" "}
</h2>
<div>
<Select
id="color"
options={lengthTypeOptions}
isMulti={false}
value={lengthType}
onChange={this.lengthTypeChange}
onBlur={this.handleBlur}
placeholder={"Select a lengthType..."}
/>
</div>
<div>
<label>Size:</label>
<input
value={this.state.size}
onChange={this.onSizeChange}
type="number"
name="size"
min="1"
placeholder="1"
required
/>
</div>
</div>
);
}
}
const App = () => (
<div className="app">
<h1>Formik Demo</h1>
<Formik
initialValues={{
querySchemaName: "",
schemas: [],
fields: [],
selectorField: "",
lengthType: "",
size: []
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
validationSchema={Yup.object().shape({
querySchemaName: Yup.string()
.required("QuerySchema name is required!")
.min(3, "Please enter a longer name")
.max(50, "Please ener a shorter name")
})}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
setFieldValue,
setTouchedValue
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="querySchemaName" style={{ display: "block" }}>
QuerySchema Name:
</label>
<input
id="querySchemaName"
placeholder="Example -- QuerySchema1"
type="text"
value={values.querySchemaName}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.querySchemaName && touched.querySchemaName
? "text-input error"
: "text-input"
}
/>
{errors.querySchemaName && touched.emaquerySchemaNameil && (
<div className="input-feedback">{errors.querySchemaName}</div>
)}
<MySelect
value={values.schemas}
onChange={setFieldValue}
options={values.schemas}
onBlur={setTouchedValue}
error={errors.topics}
touched={touched.topics}
/>
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
<MoreResources />
</div>
);
class MySelect extends React.Component {
constructor(props) {
super(props);
this.state = {
schemas: [],
fields: [],
selectorField: "",
checked: []
};
this.handleChange = this.handleChange.bind(this);
this.updateSelectorField = this.updateSelectorField.bind(this);
}
componentDidMount() {
axios.get("/dataschemas").then(response => {
this.setState({
schemas: response.data.data
});
//console.log(this.state.schemas);
});
}
handleChange = value => {
// this is going to call setFieldValue and manually update values.dataSchemas
this.props.onChange("schemas", value);
const schema = this.state.schemas.find(
schema => schema.name === value.name
);
if (schema) {
axios.get("/dataschemas/2147483602").then(response => {
this.setState({
fields: response.data.data.fields
});
//console.log("fields are: " + this.state.fields);
});
}
};
updateSelectorField = value => {
this.props.onChange("selectorField", value);
};
update = (name, value) => {
this.setState(prev => {
var arr = prev.checked;
var de = null;
for (var j = 0; j < arr.length; j++) {
if (arr[j].name === name) {
de = j;
}
}
arr[de] = Object.assign(arr[de], value);
console.log(arr);
return { checked: arr };
});
};
handleBlur = () => {
// this is going to call setFieldTouched and manually update touched.dataSchemas
this.props.onBlur("schemas", true);
};
checker = field => {
var d = -1;
for (let j = 0; j < this.state.checked.length; j++) {
if (this.state.checked[j].name === field.name) {
d = j;
}
}
if (d >= 0) {
return (
<FieldInputs
key={field.name + 1}
name={field.name}
update={this.update}
/>
);
} else {
return null;
}
};
change = e =>{
var arr = this.state.checked;
var de = -1;
for (var j = 0; j < arr.length; j++) {
if (arr[j].name === e) {
de = j;
}
}
if(de >= 0){
delete arr[de];
}else{
var arr = arr.concat([
{
name: e,
size: 1,
lengthType: "Fixed"
}
])
}
var nar = [];
for(let i=0; i<arr.length; i++){
if(typeof arr[i] !== "undefined"){
nar.push(arr[i])
}
}
this.setState({checked: nar});
}
render() {
const schemas = this.state.schemas;
const fields = this.state.fields;
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">
DataSchemas -- triggers the handle change api call - (select 1){" "}
</label>
<Select
id="color"
options={schemas}
isMulti={false}
value={schemas.find(({ name }) => name === this.state.name)}
getOptionLabel={({ name }) => name}
onChange={this.handleChange}
onBlur={this.handleBlur}
placeholder={"Pick a DataSchema..."}
/>
<label htmlFor="color">Selector Field - (select 1) </label>
<Select
id="color"
options={fields}
isMulti={false}
value={fields.find(({ name }) => name === this.state.name)}
getOptionLabel={({ name }) => name}
onChange={this.updateSelectorField}
placeholder={"Select a Selector Field..."}
/>
{!!this.props.error && this.props.touched && (
<div style={{ color: "red", marginTop: ".5rem" }}>
{this.props.error}
</div>
)}
<div>
<FieldArray
name="fields"
render={arrayHelpers => (
<div>
{fields.map(field => (
<React.Fragment>
<div key={field.name}>
<label>
<input
name="fields"
type="checkbox"
value={field.name}
onChange={(e) => {
this.change(field.name)
if (e.target.checked) {
arrayHelpers.push(field.name);
} else {
const idx = fields.indexOf(field.name);
arrayHelpers.remove(idx);
}
}}
/>{" "}
{field.name}
</label>
</div>
{this.checker(field)}
</React.Fragment>
))}
</div>
)}
/>
</div>
</div>
);
}
}
render(<App />, document.getElementById("root"));
here is the codesandbox

React - sending stored state values in post request - malformed data

I have a group of checkboxes, and when each box is checked, each one can have its own lengthType and size values that are stored to state.
I have also made a sandbox: https://codesandbox.io/s/j29yp2j905
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: ["action", "callee", "caller", "duration", "message"],
fieldNames: [],
size: {},
lengthType: {},
maxArrayElements: {}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
fieldNamesChanged = newFieldNames => {
console.log("newFiledNames", newFieldNames);
this.setState({ fieldNames: newFieldNames });
};
onChange = 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([e.target.name]);
};
updateLengthType = e => {
e.persist();
const { fieldNames } = this.state;
const lastCheckedFieldName = fieldNames[fieldNames.length - 1];
console.log("e", e);
this.setState(prevState => {
let lengthType = { ...prevState.lengthType };
lengthType[lastCheckedFieldName] = e.target.value;
return {
lengthType
};
});
console.log(this.state.lengthType);
};
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([e.target.name]);
};
handleChange = event => {
const schema = this.state.schemas.find(
schema => schema.name === event.target.value
);
if (schema) {
axios({
method: "get",
url: `${schema.selfUri}`,
headers: { Accept: " " }
})
.then(response => {
console.log(response);
this.setState({
fields: response.data.data.fields,
selectedId: response.data.data.id
});
console.log(this.state.selectedId);
console.log(this.state.fields);
})
.catch(error => console.log(error.response));
}
};
handleSubmit = event => {
event.preventDefault();
const fields = this.state.fieldNames.map(fieldName => ({
name: fieldName,
lengthType: this.state.lengthType,
size: this.state.size,
maxArrayElements: this.state.maxArrayElements
}));
axios({
method: "post",
url: `/some/url`,
headers: {
Accept: " ",
"Content-Type": "application/json"
},
data: JSON.stringify({
name: this.state.qsName,
selectorField: this.state.selectorField,
fields: fields
})
})
.then(response => {
console.log(response);
this.setState({ querySchemaId: response.data.data.id });
})
.catch(error => console.log(error.response));
};
render() {
const { fields, fieldNames } = this.state;
const lastCheckedFieldName = fieldNames[fieldNames.length - 1];
return (
<div className="App">
<h1>Checkbox Group</h1>
<div>
<form onSubmit={this.handleSubmit}>
<fieldset>
<legend>Choose field names</legend>
<br />
<CheckboxGroup
checkboxDepth={5}
name="fieldNames"
value={this.state.fieldNames}
onChange={this.fieldNamesChanged}
>
{fields &&
fields.map(field => {
return (
<label>
<Checkbox value={field} />
{field}
</label>
);
})}
<br />
</CheckboxGroup>
<br />
{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>
)}
<br />
{lastCheckedFieldName && (
<div>
<label>Size:</label>
<input
value={this.state.size[lastCheckedFieldName] || 1}
onChange={this.onChange}
type="number"
name="size"
placeholder="1"
min="0"
required
/>
</div>
)}
<br />
{lastCheckedFieldName && (
<div>
<label>MaxArray Elements:</label>
<input
value={
this.state.maxArrayElements[lastCheckedFieldName] || 1
}
onChange={this.onChangeMaxArrayElements}
type="number"
name="maxArrayElements"
placeholder="1"
min="0"
max="100"
required
/>
</div>
)}
</fieldset>
<div className="btn-group">
<span className="input-group-btn">
<button handleSubmit={this.handleSubmit} type="submit">
Submit
</button>
<button
handleCancel={this.handleCancel}
type="reset"
onClick={() => {
alert("Clearing current field values.");
}}
>
Reset
</button>
</span>
</div>
</form>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
When I submit this form data something strange is happening with the state values being sent.
Data I’m sending has become malformed since changing a few things:
"data":
"{\"name\":\"QS7\",
\"selectorField\":\"callee\",
\"fields\":
[{\"name\":\"action\",
\"lengthType\":{\"action\":\"fixed\"},
\"size\":{\"action\":\"3\"},
\"maxArrayElements\":{\"action\":\"4\"}
}]}"
Should look like this
"data":
"{\"name\":\"QS7\",
\"selectorField\":\"callee\",
\"fields\":
[{\"name\":\"action\",
\"lengthType\":\"fixed\",
\"size\":\"3\"},
\"maxArrayElements\":\"4\"}
}]}"
field.name is being added as the first value in an array to each field element for some reason.
You store lengthType, size and maxArrayElements in a form like { fieldName: value } inside your state. So you probably forgot to pick each of them for the current fieldName inside your mapping callback in handleSubmit method. So fields should basically be mapped like this:
const fields = this.state.fieldNames.map(fieldName => ({
name: fieldName,
lengthType: this.state.lengthType[fieldName],
size: this.state.size[fieldName],
maxArrayElements: this.state.maxArrayElements[fieldName]
});

React - how to pass props to grandson component?

I am aware of similar threads here, but any of them still can't help me.
I'm trying to pass deleteItem() function from parent component to onClick argument in grandson component.
Please, look at components and tell me what is wrong, what should I change to access this function in grandson component?
Parent - https://codeshare.io/2E39oO
Child - https://codeshare.io/5XnwN8
Grandson - https://codeshare.io/5z9JXE
Here are the two things I spotted
misspelling in deleteHandler (already mentioned)
the button was disabled, so it wouldn't trigger an event
Example I ended up with
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{
title: "Cup cleaning",
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: "Smoking rollton",
todo: "Do some rollton and cigarettes"
},
{
title: "Curious dream",
todo: "Build a time machine"
}
],
title: "",
todo: ""
};
}
createNewToDoItem = () => {
this.setState(({ list, title, todo }) => ({
list: [
...list,
{
title,
todo
}
],
title: "",
todo: ""
}));
};
handleKeyPress = e => {
if (e.target.value !== "") {
if (e.key === "Enter") {
this.createNewToDoItem();
}
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value
});
};
deleteItem(indexToDelete) {
console.log("HERE");
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
}
editItem = (i, updTitle, updToDo) => {
let arr = this.state.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState({ list: arr });
};
eachToDo = (item, i) => {
return (
<ToDoItem
key={i}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
);
};
render() {
return (
<div className="ToDo">
<h1 className="ToDo-Header" />
<div className="ToDo-Container">
<div className="ToDo-Content">
{this.state.list.map(this.eachToDo)}
</div>
<div>
<input
type="text"
placeholder="Enter new title"
value={this.state.title}
onChange={this.handleTitleInput}
onKeyPress={this.handleKeyPress}
/>
<input
type="text"
placeholder="Enter new todo"
value={this.state.todo}
onChange={this.handleTodoInput}
onKeyPress={this.handleKeyPress}
/>
{/* <AddButton addHandler={this.createNewToDoItem} /> */}
</div>
</div>
</div>
);
}
}
class ToDoItem extends Component {
constructor(props) {
super(props);
this.state = {
editMode: false
};
}
edit = () => {
this.setState({ editMode: true });
};
save = () => {
let updTitle = this.refs.newTitle.value;
let updToDo = this.refs.newToDo.value;
this.props.editItem(updTitle, updToDo);
this.setState({
editMode: false
});
};
renderNormal = () => {
return (
<div className="ToDoItem">
<p className="ToDoItem-Text">{this.props.title}</p>
<p className="ToDoItem-Text">{this.props.todo}</p>
{/* <EditButton editHandler={this.edit} /> */}
<FloatingActionButtons deleteHandler={this.props.deleteItem} />
{/* <button className="ToDoItem-Button" id="editbtn" onClick={this.edit}>✍</button> */}
{/* <button className="ToDoItem-Button" id="delbtn" onClick={this.props.deleteItem}>−</button> */}
</div>
);
};
renderEdit = () => {
return (
<div className="ToDoItem">
<textarea ref="newTitle" defaultValue={this.props.title} />
<textarea ref="newToDo" defaultValue={this.props.todo} />
<button onClick={this.save} className="ToDoItem-Button" id="savebtn">
💾
</button>
</div>
);
};
render() {
if (this.state.editMode) {
return this.renderEdit();
} else {
return this.renderNormal();
}
}
}
const styles = theme => ({
button: {
margin: theme.spacing.unit
}
});
function FloatingActionButtons(props) {
return (
<div>
<Button variant="fab" aria-label="Delete" onClick={props.deleteHandler}>
Delete
</Button>
</div>
);
}
FloatingActionButtons.propTypes = {
classes: PropTypes.object.isRequired
};

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