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
Related
I am trying to run a function on a Todo list that when a todo is added. A function will check the input box to check if its empty and if its empty, not do anything.
However, after using the function to check if the input is empty, it always returns False even when its empty. Where is the bug here?
The function name in question is "checkInput()" and it runs from the main submit button on the page
import React from "react";
import "./App.css";
import { isTemplateElement } from "#babel/types";
class TodoListt extends React.Component {
state = {};
constructor(props) {
super(props);
this.state = {
userInput: "",
list: [],
};
}
changeUserInput(input) {
this.setState({
userInput: input
})
}
addToList() {
const { list, userInput } = this.state;
this.setState({
list: [...list, {
text: userInput, key: Date.now(), done: false
}],
userInput: ''
})
}
handleChecked(e, index) {
console.log(e.target.checked);
const list = [...this.state.list];
list[index] = { ...list[index] };
list[index].done = e.target.checked;
this.setState({
list
})
}
checkInput() {
console.log(this.state.userInput);
userInput: '' ? console.log("True") : console.log("False")
}
render() {
return (
<div className="to-do-list-main">
<input
onChange={(e) => this.changeUserInput(e.target.value)}
value={this.state.userInput}
type="text"
/>
<button onClick={() => { this.checkInput(); { this.addToList(this.state.userInput) } }}>Add todo</button>
{this.state.list.map((list, index) => (
<div className="form">
<ul>
<li><input type="checkbox" onChange={(e) => this.handleChecked(e, index)} />
<span style={{ textDecoration: list.done ? 'line-through' : 'inherit' }}>
{list.text}
</span>
</li>
</ul>
</div>
))}
</div>
);
}
}
export default TodoListt;
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
}
});
};
I'm building a website with react. I create a component which has grouped Textfield , I don't know how to set the value of those Textfield to the state.
The state format should be like : state:{products:[{},{},{}]}
I've tried to create a button to insert new group of Textfield, and an handleChange method to capture the Textvalue,
but still stuck in how to set states.
export default class extends Component {
state = {
count: 0,
products: []
};
handleAddClick = () => {
this.setState(({count}) => ({
count: count + 1
}))
};
handleChange = e => {
//this is where i stuck
};
render() {
const {count} = this.state;
let items = [];
for (let i = 0; i <= count; i++) {
items.push(
<div key={i}>
<TextField
label="product"
margin="normal"
onChange={this.handleChange}
/>
<TextField
label="color"
margin="normal"
onChange={this.handleChange}
/>
<TextField
label="quantity"
margin="normal"
onChange={this.handleChange}
/>
<TextField
label="price"
margin="normal"
onChange={this.handleChange}
/>
</div>
)
}
return <Fragment>
<Button onClick={this.handleAddClick}>
<AddIcon/>
</Button>
{items}
</Fragment>
}
}
I realize that I have to use some id to identify the different group of TextFields, but where to put it , and how to get it in handleChange method?
Your state seems wrong, you need add one more key which handles input changes, lets call it as product. So, this product will handle the current textboxes and once user click add button you can add that to your products array. this produce will an object.
state = {
count: 0,
product:{},
products: [],
};
pass the textfield value with keys so you can fill the product object key,
<TextField value={this.state.product.product}
label="product"
type="text"
margin="normal"
onChange={(e) => { this.handleChange(e, 'product') }}
/>
And set it like this
handleChange = (e, type) => {
this.setState({
product: {
...this.state.product,
[type]: e.target.value,
},
});
};
And when user clicks on add you can push this produce to products array
handleAddClick = () => {
this.setState({
products: this.state.products.concat(this.state.product),
product: {},
})
};
Here is how your component will look:
Replace input with TextField and button with Button
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
class App extends Component {
state = {
count: 0,
product: {},
products: [],
};
handleAddClick = () => {
this.setState({
products: this.state.products.concat(this.state.product),
product: {
product: "",
color: "",
quantity: "",
price: "",
}
})
};
handleChange = (e, type) => {
//this is where i stuck
this.setState({
product: {
...this.state.product, [
type]: e.target.value,
}
});
};
render() {
console.log(this.state);
const { count } = this.state;
let items = [];
for (let i = 0; i <= count; i++) {
items.push(
<div key={i}>
<input value={this.state.product.product}
label="product" type="text"
margin="normal"
onChange={(e) => { this.handleChange(e, 'product') }}
/>
<input value={this.state.product.color}
label="color"
margin="normal"
onChange={(e) => { this.handleChange(e, 'color') }}
/>
<input value={this.state.product.quantity}
label="quantity"
margin="normal"
onChange={(e) => { this.handleChange(e, 'quantity') }}
/>
<input value={this.state.product.price}
label="price"
margin="normal"
onChange={(e) => { this.handleChange(e, 'price') }}
/>
</div>
)
}
return (
<div>
<button onClick={this.handleAddClick}>
add
</button>
{items}
</div>
)
}
}
render(<App />, document.getElementById('root'));
EDIT:
As you want to iterate over products and change the values,You do not need product any more we will use the product as base values for the products, now following things will be changed, you need to get index of each products and by default we will assign one value
like this
let product = {
product: "",
color: "",
quantity: "",
price: "",
};
class App extends Component {
state = {
products: [Object.assign({},product)],
};
handleAddClick = () => {
var newProduce = {
product: "",
color: "",
quantity: "",
price: "",
}
this.setState({
products: this.state.products.concat(newProduce),
})
};
handleChange = (e, type, index) => {
const copiedData = Object.assign({}, this.state);
copiedData.products[index][type] = e.target.value;
this.setState(copiedData);
};
...
Here is the demo
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]
});
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
};