Modfying state in React using Onclick - javascript

im very new to React, just wanted to make a simple program that has multiple buttons that correspond to some value in state, what i want is when a button is pressed, the respective value negates
class Parent extends React.Component{
constructor(props){
super(props);
this.state={
a: true,
b: false,
c: true,
d: false
}
this.handleAction = this.handleAction.bind(this);
}
handleAction(action) {
this.setState({
name: !this.state.name
});
}
render(){
return(
<div>
<Child onAction={this.handleAction} name='a'/>
<Child onAction={this.handleAction} name='b'/>
<Child onAction={this.handleAction} name='c'/>
<Child onAction={this.handleAction} name='d'/>
</div>
)
}
}
function Child({onAction, name}){
return (
<button onClick={onAction}>
{name}
</button>
);
}
is there a way to do this without 4 separate handleAction functions ? i tried passing name to onAction but that throws Error: Maximum update depth exceeded
i know there must be something really obvious that i'm missing, thank you for your help.

You could pass the name as a parameter to your handleAction function
....
handleAction(name) {
this.setState( state => ({ [name]: !state[name] }) );
}
render(){
return(
<div>
<Child onAction={() => this.handleAction('a')} name='a'/>
<Child onAction={() => this.handleAction('b')} name='b'/>
<Child onAction={() => this.handleAction('c')} name='c'/>
<Child onAction={() => this.handleAction('d')} name='d'/>
</div>
)
}
...
Note that reading from this.state inside this.setState isn't good practice. see here

Change the handler so that it can accept name and return another function.
handleAction(name) {
return () => {
this.setState({
[name]: !this.state[name],
});
};
}
In child component pass the name of that child
<Child onAction={this.handleAction("a")} name='a'/>

Try this out:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
a: true,
b: false,
c: true,
d: false,
};
this.handleAction = this.handleAction.bind(this);
}
handleAction(name) {
return () => {
console.log("name", name);
this.setState({
[name]: !this.state[name],
});
};
}
render() {
console.log("this.state", this.state);
return (
<div>
<Child onAction={this.handleAction} name="a" />
<Child onAction={this.handleAction} name="b" />
<Child onAction={this.handleAction} name="c" />
<Child onAction={this.handleAction} name="d" />
</div>
);
}
}
function Child({ onAction, name }) {
return <button onClick={onAction(name)}>{name}</button>;
}

As I see you want to use the name of the button and then to change the state with the same name. First, you should provide the name of the button to the function. As the event is passed to the function by default, you can get the name for example like this:
event.target.innerText
Then, if you use the name as variable containing the value you must use [name] notation. Otherwise, it will add the 'name' property to your object.
As the result your handler should look like this:
handleAction(e) {
const name = e.target.innerText
this.setState({
[name]: !this.state[name]
});
}

Related

Passing handleSubmit() to child component does not modify parent's state

I am new to React and Javascript.
I am trying to have a user fill in a form that describes what a "Mob" should look like. When the user hits submit, I expect handleSubmit() (passed in through a parent) to modify the parent's state, which is an object. However, this behavior is not happening.
Here is the parent component, called App.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
mob: new Mob("", "")
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
alert("A name was submitted: " + this.state.vnum + " event value: " + event.state.vnum);
const newMob = new Mob(event.state.vnum, event.state.shortDesc);
this.setState({
mob: newMob
});
}
render() {
return (
<div>
<MobForm mob={this.state.mob} onSubmit={() => this.handleSubmit} />
{console.log("parsed mob vnum: " + this.state.mob.vnum)}
</div>
);
}
}
The child component, called MobForm
class MobForm extends React.Component {
render() {
return (
<div>
<form onSubmit={this.props.onSubmit}>
<CreateStringInputField
name="vnum"
label="vnum:"
/>
<CreateStringInputField
name="shortDesc"
label="Short Desc:"
/>
<input type="submit" value="Submit" />
</form>
{console.log(this.state)}
</div>
);
}
}
Which is calling CreateStringInputField()
function CreateStringInputField(props) {
return (
<div name="row">
<label>
<b>{props.label}</b>
<br />
<input
type="text"
name={props.name}
label={props.label}
/>
</label>
</div>
);
}
And, in case it matters, here is what "Mob" looks like.
class Mob {
constructor(vnum, shortDesc) {
this.vnum = vnum;
this.shortDesc = shortDesc;
};
}
I expect to see {console.log("parsed mob vnum: " + this.state.mob.vnum)} print out the vnum as entered by a user. Instead, I see nothing. How can I achieve this expected output?
With React you won't need to work with plain classes. Instead, the class extends a provided React component (Component or PureComponent) or if you don't need state, then'll use plain functions that just return some JSX.
Working example: https://codesandbox.io/s/simple-form-kdh3w
index.js
import React from "react";
import { render } from "react-dom";
import MobForm from "./components/MobForm";
// simple function that returns "MobForm" and it gets rendered by ReactDOM
function App() {
return <MobForm />;
}
// applies "App" to a <div id="root"></div> in the public/index.html file
render(<App />, document.getElementById("root"));
components/MobForm/index.js (stateful parent component)
import React, { Component } from "react";
import Form from "../Form";
const initialState = {
vnum: "",
shortDesc: ""
};
// a stateful parent that manages child state
class MobForm extends Component {
constructor(props) {
super(props);
this.state = initialState;
// since the class fields are normal functions, they'll lose context
// of "this" when called as a callback. therefore, they'll need
// to be bound to "this" -- via bind, "this" is now referring to
// the Class, instead of the global window's "this")
this.handleChange = this.handleChange.bind(this);
this.handleReset = this.handleReset.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// a reusable class field that stores an input's value via its "name"
// for example: [vnum]: "12345", [shortDesc]: "A number"
// using object destructuring for shorter syntax:
// [event.target.name]: event.target.value
handleChange({ target: { name, value } }) {
this.setState({ [name]: value });
}
// a class field to reset state
handleReset() {
this.setState(initialState);
}
// a class field to "submit" the form and alert what's currently in state
handleSubmit(event) {
// preventDefault prevents page refreshes
event.preventDefault();
// JSON.stringify allows you to print the contents of an object
// otherwise, you'll just see [object Object]
alert(JSON.stringify(this.state, null, 4));
// clears state after submitting form
this.handleReset();
}
render() {
return (
// passing down state via the spread operator, shorthand for
// "vnum={this.state.vum}" and "shortDesc={this.state.shortDesc}",
// as well as, passing down the class fields from above
<Form
{...this.state}
handleChange={this.handleChange}
handleReset={this.handleReset}
handleSubmit={this.handleSubmit}
/>
);
}
}
export default MobForm;
components/Form/index.js (a child function that returns some form JSX)
import React from "react";
import PropTypes from "prop-types";
import Input from "../Input";
// using object destructuring to pull out the MobForm's passed down
// state and fields. shorthand for using one parameter named "props"
// and using dot notation: "props.handleChange", "props.handleReset", etc
function Form({ handleChange, handleReset, handleSubmit, shortDesc, vnum }) {
return (
<form style={{ width: 200, margin: "0 auto" }} onSubmit={handleSubmit}>
<Input name="vnum" label="vnum:" value={vnum} onChange={handleChange} />
<Input
name="shortDesc"
label="Short Desc:"
value={shortDesc}
onChange={handleChange}
/>
<button type="button" onClick={handleReset}>
Reset
</button>{" "}
<button type="submit">Submit</button>
</form>
);
}
// utilizing "PropTypes" to ensure that passed down props match
// the definitions below
Form.propTypes = {
handleChange: PropTypes.func.isRequired,
handleReset: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
shortDesc: PropTypes.string,
vnum: PropTypes.string
};
export default Form;
components/Input/index.js (a reuseable input function)
import React from "react";
import PropTypes from "prop-types";
// once again, using object destructuring to pull out the Form's
// passed down state and class fields.
function Input({ label, name, value, onChange }) {
return (
<div name="row">
<label>
<b>{label}</b>
<br />
<input
type="text"
name={name}
label={label}
value={value}
onChange={onChange}
/>
</label>
</div>
);
}
// utilizing "PropTypes" to ensure that passed down props match
// the definitions below
Input.propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string,
onChange: PropTypes.func.isRequired
};
export default Input;
In this line
<MobForm mob={this.state.mob} onSubmit={() => this.handleSubmit} />
you are defining an anonymous function that returns your handleSubmit function.
In your form
<form onSubmit={this.props.onSubmit}>
onSubmit will execute the this.props.onSubmit which just returns the handleSubmit function but it wont execute it. To fix it just change MobForm to pass handleSubmit directly instead of passing it in an anonymous function:
<MobForm mob={this.state.mob} onSubmit={this.handleSubmit} />
To handle the submission correctly you need to convert your form inputs to managed components. See docs here
Something like this would be a good start:
class MobForm extends React.Component {
constructor(props) {
super(props);
this.state = {
vnum: '',
shortDesc: '',
};
this.handleChangeVnum = this.handleChangeVnum.bind(this);
this.handleChangeShortDesc = this.handleChangeShortDesc.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeVnum(event) {
this.setState({vnum: event.target.value});
}
handleChangeShortDesc(event) {
this.setState({shortDesc: event.target.value});
}
handleSubmit(event) {
this.props.onSubmit(this.state);
event.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CreateStringInputField
name="vnum"
label="vnum:"
value={this.state.vnum}
onChange={this.handleChangeVnum}
/>
<CreateStringInputField
name="shortDesc"
label="Short Desc:"
value={this.state.shortDesc}
onChange={this.handleChangeShortDesc}
/>
<input type="submit" value="Submit" />
</form>
{console.log(this.state)}
</div>
);
}
}
And update CreateStringInputField()
function CreateStringInputField(props) {
return (
<div name="row">
<label>
<b>{props.label}</b>
<br />
<input
type="text"
name={props.name}
label={props.label}
value={props.value}
onChange={props.onChange}
/>
</label>
</div>
);
}
I was able to get my desired behavior by passing a function to MobForm which updates this.state.mob.
App
class App extends React.Component {
state = {
mob: new Mob("", "")
};
updateMob = newMob => {
this.setState({
mob: newMob
});
};
render() {
return (
<div>
<MobForm mob={this.state.mob} onSubmit={this.updateMob} />
</div>
);
}
}
I then made MobForm maintain vnum, shortDesc state that I could use in my onChange()
MobForm
state = { vnum: "", shortDesc: "" };
handleSubmit = event => {
event.preventDefault();
const mob = new Mob(this.state.vnum, this.state.shortDesc);
this.props.onSubmit(mob);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CreateStringInputField
name="vnum"
value={this.state.vnum}
onChange={event => this.setState({ vnum: event.target.value })}
/>
<CreateStringInputField
name="short desc"
value={this.state.shortDesc}
onChange={event => this.setState({ shortDesc: event.target.value })}
/>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

Passing onChange events through the state

I am creating a form component and I want to be able to pass on change elements per form element and I cannot seem to get it work properly.
I have my LoginComponent
import React from "react";
import './LoginComponent.css';
import FormComponent from '../FormComponent/FormComponent';
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
header: "Login",
id: "login-form",
name: "login-form",
items: [
{
element: "input",
type: "email",
id: "lf-email",
name: "lf-email",
value: "",
onChange: this.handleOnChangeEmail.bind(this),
placeholder: "Email",
img: {},
},
{
element: "input",
type: "password",
id: "lf-password",
name: "lf-password",
value: "",
onChange: ,
placeholder: "Password",
img: {},
},
{
element: "checkbox",
id: "lf-remember-me",
name: "lf-remember-me",
value: "lf-remember-me",
onChange: ,
display: "Remember Me",
isSelected: false
}
]
}
}
// Figure out how to pass onChange functions per item in state.
handleOnChangeEmail(e) {
console.log("Email changed");
}
render() {
return (
<div className="LoginComponent">
{/*
Create onSubmit function for submitting the form
Create handle change functions for inputs
*/}
<FormComponent id={ this.state.id } name={ this.state.name } onSubmit="" header={ this.state.header } items={ this.state.items } />
</div>
);
}
}
export default LoginComponent;
As you can see I want to pass in the handle function in the state of the component so I can uniquely handle form inputs. When I run this code though it fails because I cannot pass a function in the state. Is this type of request allowed or able to accomplish in another way?
I know you can pass the bound function to the component directly but the form component is dynamically built based on the state.item array.
Here is my form component
import React from "react";
import './FormComponent.css';
import InputComponent from './InputComponent/InputComponent';
import FormHeaderComponent from './FormHeaderComponent/FormHeaderComponent';
import CheckboxComponent from "./CheckboxComponent/CheckboxComponent";
class FormComponent extends React.Component {
render() {
const formItems = this.props.items.map((item) => {
switch(item.element) {
case "input":
return <InputComponent type={ item.type } id={ item.id } name={ item.name } placeholder={ item.placeholder } value={ item.value } onChange={ item.onChange } />
case "checkbox":
return <CheckboxComponent id={ item.id } name={ item.name } value={ item.value } selected={ item.isSelected } onChange={ item.onChange } display={ item.display } />
default:
return <InputComponent />;
}
});
return (
<form id={ this.props.id } name={ this.props.name }>
<FormHeaderComponent header={ this.props.header } />
{/*
Setup handling of submit functions
Setup handling of onchange function for inputs
*/}
{ formItems }
</form>
);
}
}
export default FormComponent;
As you can see in the formItems I am trying to create the elements with the onChange function from the state passed in. Any help or suggestions would be appreciated. I am well aware also I can just make the form component a component that loads all children passed so you basically build the form in the login component without state but I would prefer that not be the case.
Storing methods in state to be passed to children is usually a bad idea. I propose an alternative.
Consider the onChange function, that will still reside in LoginComponent and will be passed as a prop to the <FormComponent />
The handler is written as such that you'll be able to identify which child component called it, by taking it's name attribute too, eliminating the need to make a unique handler for each input.
LoginComponent:
handleOnChangeEmail(e) {
const { name, value } = e.target;
console.log(`${name} changed to ${value}`);
}
render() {
return (
<div className="LoginComponent">
<FormComponent
id={this.state.id}
name={this.state.name}
onSubmit=""
header={this.state.header}
items={this.state.items}
formOnChange={this.handleOnChangeEmail}
/>
</div>
);
}
Then when you are iterating over and constructing your InputComponent, pass this to it's onChange:
FormComponent:
class FormComponent extends React.Component {
render() {
const formItems = this.props.items.map(item => {
switch (item.element) {
case "input":
return (
<InputComponent
type={item.type}
id={item.id}
name={item.name}
placeholder={item.placeholder}
value={item.value}
onChange={this.props.formOnChange}
/>
);
case "checkbox":
return (
<CheckboxComponent
id={item.id}
name={item.name}
value={item.value}
selected={item.isSelected}
onChange={this.props.formOnChange}
display={item.display}
/>
);
default:
return <InputComponent />;
}
});
return (
<form id={this.props.id} name={this.props.name}>
<FormHeaderComponent header={this.props.header} />
{formItems}
</form>
);
}
}
PS: Don't forget to bind your functions or use arrow functions.
Try implementing same function handleChange to handle all form value change.
React onChange function pass event argument. With event argument perform necessary logic. Do not need onChange key in form object in state.
// use lodash library for array handling.
// yarn add lodash
// import {indexOf} from "lodash"
// using fat arrow will save from binding function in constructor.
handleChange=({target})=> {
const {items} = this.state;
// target.name/id will give the element state can be update as required.
let index = indexOf(items, {name:target.name})
items[index].value = target.value;
this.setState({
items
})
}
// remove handle change from items and just user it for all form value change.
<FormComponent id={ this.state.id } name={ this.state.name } handleChange={this.handleChange} onSubmit="" header={ this.state.header } items={ this.state.items } />

ReactJS instant Search with input

Im making my first react project. Im new in JS, HTML, CSS and even web app programming.
What i want to do it is a Search input label. Now its look like this:
Like you can see i have some list of objects and text input.
I Have two components, my ProjectList.js with Search.js component...
class ProjectsList extends Component {
render() {
return (
<div>
<Search projects={this.props.projects} />
<ListGroup>
{this.props.projects.map(project => {
return <Project project={project} key={project.id} />;
})}
</ListGroup>
</div>
);
}
}
export default ProjectsList;
... and ProjectList.js displays Project.js:
How looks Search.js (its not ended component)
class Search extends Component {
state = {
query: ""
};
handleInputChange = () => {
this.setState({
query: this.search.value
});
};
render() {
return (
<form>
<input
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
<p />
</form>
);
}
}
export default Search;
My project have name property. Could you tell me how to code Search.js component poperly, to change displaying projects dynamically based on input in text label? for example, return Project only, if text from input match (i want to search it dynamically, when i start typing m... it shows all projects started on m etc).
How to make that Search input properly? How to make it to be universal, for example to Search in another list of objects? And how to get input from Search back to Parent component?
For now, in react dev tools whatever i type there i get length: 0
Thanks for any advices!
EDIT:
If needed, my Project.js component:
class Project extends Component {
state = {
showDetails: false
};
constructor(props) {
super(props);
this.state = {
showDetails: false
};
}
toggleShowProjects = () => {
this.setState(prevState => ({
showDetails: !prevState.showDetails
}));
};
render() {
return (
<ButtonToolbar>
<ListGroupItem className="spread">
{this.props.project.name}
</ListGroupItem>
<Button onClick={this.toggleShowProjects} bsStyle="primary">
Details
</Button>
{this.state.showDetails && (
<ProjectDetails project={this.props.project} />
)}
</ButtonToolbar>
);
}
}
export default Project;
To create a "generic" search box, perhaps you could do something like the following:
class Search extends React.Component {
componentDidMount() {
const { projects, filterProject, onUpdateProjects } = this.props;
onUpdateProjects(projects);
}
handleInputChange = (event) => {
const query = event.currentTarget.value;
const { projects, filterProject, onUpdateProjects } = this.props;
const filteredProjects = projects.filter(project => !query || filterProject(query, project));
onUpdateProjects(filteredProjects);
};
render() {
return (
<form>
<input onChange={this.handleInputChange} />
</form>
);
}
}
This revised version of Search takes some additional props which allows it to be reused as required. In addition to the projects prop, you also pass filterProject and onUpdateProjects callbacks which are provided by calling code. The filterProject callback allows you to provide custom filtering logic for each <Search/> component rendered. The onUpdateProjects callback basically returns the "filtered list" of projects, suitable for rendering in the parent component (ie <ProjectList/>).
The only other significant change here is the addition of visibleProjects to the state of <ProjectList/> which tracks the visible (ie filtered) projects from the original list of projects passed to <ProjectList/>:
class Project extends React.Component {
render() {
return (
<div>{ this.props.project }</div>
);
}
}
class ProjectsList extends React.Component {
componentWillMount() {
this.setState({ visibleProjects : [] })
}
render() {
return (
<div>
<Search projects={this.props.projects} filterProject={ (query,project) => (project == query) } onUpdateProjects={ projects => this.setState({ visibleProjects : projects }) } />
<div>
{this.state.visibleProjects.map(project => {
return <Project project={project} key={project.id} />;
})}
</div>
</div>
);
}
}
class Search extends React.Component {
componentDidMount() {
const { projects, filterProject, onUpdateProjects } = this.props;
onUpdateProjects(projects);
}
handleInputChange = (event) => {
const query = event.currentTarget.value;
const { projects, filterProject, onUpdateProjects } = this.props;
const filteredProjects = projects.filter(project => !query || filterProject(query, project));
onUpdateProjects(filteredProjects);
};
render() {
return (
<form>
<input onChange={this.handleInputChange} />
</form>
);
}
}
ReactDOM.render(
<ProjectsList projects={[0,1,2,3]} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I will assumes both your Search and ProjectList component have a common parent that contains the list of your projects.
If so, you should pass a function into your Search component props, your Search component will then call this function when the user typed something in the search bar. This will help your parent element decide what your ProjectsLists needs to render :
handleInputChange = () => {
this.props.userSearchInput(this.search.value);
this.setState({
query: this.search.value
});
};
And now, here is what the parent element needs to include :
searchChanged = searchString => {
const filteredProjects = this.state.projects.filter(project => project.name.includes(searchString))
this.setState({ filteredProjects })
}
With this function, you will filter out the projects that includes the string the user typed in their names, you will then only need to put this array in your state and pass it to your ProjectsList component props
You can find the documentation of the String includes function here
You can now add this function to the props of your Search component when creating it :
<Search userSearchInput={searchChanged}/>
And pass the filtered array into your ProjectsList props :
<ProjectsList projects={this.state.filteredProjects}/>
Side note : Try to avoid using refs, the onCHnage function will send an "event" object to your function, containing everything about what the user typed :
handleInputChange = event => {
const { value } = event.target
this.props.userSearchInput(value);
this.setState({
query: value
});
};
You can now remove the ref from your code

Create price range fields (min and max) using reactJS

after spending a lot time I need help here.
I am creating a template. Where I can use different form fields. Everything is working. I am getting value form all fields as expected.
But problem is when I have 2 input fields together in one component and pass this component to parent component and get the value. then save this value in object.
I hope its clear enough. Thank you in advance
class Parent extends Component {
handlePriceMinMax(event) {
console.log(event.target.value);
/* I cant set the right values here.
* These values get overwritten with latest value.
* Because I get value on keypress/onChange value */
this.setState({
priceRange: {
min: event.target.value,
max: event.target.value
}
}
);
}
render() {
return (
<Child onChange={this.handlePriceMinMax}/>
)
}
}
class Child extends Component {
render() {
return (
<div className="form-group">
<label>Price</label>
<input type="number" onChange={this.props.onChange}/>
<input type="number" onChange={this.props.onChange}/>
</div>
);
}
}
export default Child;
https://codesandbox.io/s/zr30923nrm
I think this should work.
So basically you need to tell to the handlePriceMinMax what value do you want to update.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { priceRange: { min: 0, max: 0 } };
}
handlePriceMinMax = (value, event) => {
const priceRange = this.state.priceRange;
priceRange[value] = event.target.value;
this.setState({ priceRange });
};
render() {
return <Child onChange={this.handlePriceMinMax} />;
}
}
class Child extends React.Component {
render() {
return (
<div className="form-group">
<label>Price</label>
<input type="number" onChange={this.minChange} />
<input type="number" onChange={this.maxChange} />
</div>
);
}
minChange = e => {
this.props.onChange("min", e);
};
maxChange = e => {
this.props.onChange("max", e);
};
}
I changed my code, you can create function in child first that get target value and pass to props function
class Child extends Component {
submitInput(e) {
this.props.onChange(e)
}
render() {
return (
<div className="form-group">
<label>Price</label>
<input type="number" onChange={(e) => this.submitInput(e)}/>
<input type="number" onChange={(e) => this.submitInput(e)}/>
</div>
);
}
}
export default Child;

React: TypeError: Unable to get property 'handler' of undefined or null reference

I am new to React.js and cannot seem to get any event handlers to work. I get the above-referenced error anytime I make an event handler outside of the render function instead of as an annonymous inner class. Can someone point out what I'm doing wrong please?
class Checkboxes extends Component{
constructor(props) {
super(props);
this.state = { checked: [true, true],
frame: [values, values],
title: ['A', 'B'] };
this.handleChange= this.handleChange.bind(this);
}
handleChange(e) {
let index= e.target.id;
let newState = this.state.checked.slice();
newState[index] = !this.state.checked[index];
this.setState ({checked: newState});
}
render(){
const selectionBoxes = this.state.title.map(function(title, index) {
return (
<div className="w3-checkbox" id={index} onClick={this.handleChange}>
<input
type="checkbox"
label={'Division '+title}
value={this.state.checked[index]}
checked={!this.state.checked[index]}
onChange= {this.handleChange}
id={index} />
<label id={index} onClick={this.handleChange}>
{'Division '+ title}
</label>
</div>
);
});
const frameDisplay = this.state.frame.map(function(frame, index) {
return (
<div>
{this.state.checked[index]} ? null :
<DivFrame frameId={frame} width={this.state.width} height={this.state.height} title={this.state.title[index]} />
</div>
);
});
return (
{selectionBoxes}
);
}
};
export default Checkboxes;
It seems that, even though you may have bound the handleChange to this in the constructor, it is still not in scope within the this.state.title.map(function(title, index) {... function. I have fixed this problem by simply changing this.state.title.map(function(title, index) {... to this.state.title.map((title, index) => {...
This is the ES6 syntax and does the binding for me automatically.
Have a look here for a demo: https://codesandbox.io/s/py2zp8z1kj
Please note that for the sake of simplicity I have removed the code pertaining to the frames component since it's not really related to the problem and to cut down on the work required having to implement that component. To have the event handler working on that component as well though you will have to change the callback of the mapping function to the ES6 syntax as well.
Also, you will notice that in the return of the render() method, I have removed the curly braces because you do not need them since selectionBoxes is raw html and needs not to be evaluated. Actually evaluating it ({ selectionBoxes}) converts the value of selectionBoxes to an object which is NOT a valid react child. So rather put selectionBoxes as is in the render block.
In case you are not able to view the demo, this is how the code looks like:
class Checkboxes extends Component {
constructor(props) {
super(props);
this.state = {
checked: [true, true],
frame: [0, 1],
title: ['A', 'B']
};
}
handleChange(e) {
let index = e.target.id;
let newState = this.state.checked.slice();
newState[index] = !this.state.checked[index];
this.setState({ checked: newState });
}
render() {
const selectionBoxes = this.state.title.map( (title, index) => {
return (
<div className="w3-checkbox" id={index} onClick={this.handleChange}>
<input
type="checkbox"
label={'Division ' + title}
value={this.state.checked[index]}
checked={!this.state.checked[index]}
onChange={this.handleChange}
id={index} />
<label id={index} onClick={this.handleChange}>
{'Division ' + title}
</label>
</div>
);
});
return (
selectionBoxes
);
}
};
export default Checkboxes;

Categories

Resources