I have multiple checkbox in my React Class B.js:
<input
type="checkbox"
inline={true}
checked={this.props.checkBoxDefaultStatus}
onChange={this.handleCheckBoxClick}
/>
Now the prop checkBoxDefaultStatus is passed from parent class A.js.
A.js
this.state = {
checkBoxDefaultStatus: false
}
handleMultiSelect() {
this.setState({
checkBoxDefaultStatus: true
})
}
render() {
<B checkBoxDefaultStatus={this.state.checkBoxDefaultStatus} />
}
EDIT: Now All my child checkboxes are getting checked when I click on parent checkbox, but the issue is that my child checked boxes checked status does not change when I click on them as they are already set by parent prop. I need some way to maintain this also.
This is the behaviour I want https://stackoverflow.com/a/35218069/6574017
If you want change parent component state inside child component, then you have to pass parent component method to child component as props like below,
<B handleCheckBoxClick={this.handleMultiSelect}/>
Check below working code. I build 2 component for your scenario.
class B extends React.Component {
constructor(){
super();
this.state = {
checkBoxClick : {
1: false,
2: false
}
}
this.handleCheckBoxClick = this.handleCheckBoxClick.bind(this);
}
handleCheckBoxClick(no, event){
//console.log('no', no);
//console.log('event.target.value', event);
var checkBoxClick = this.state.checkBoxClick;
checkBoxClick[no] = !this.state.checkBoxClick[no];
this.setState({
checkBoxClick
});
var alltrue =Object.keys(checkBoxClick).every((k) =>{ return checkBoxClick[k] });
//console.log('alltrue', alltrue);
if(alltrue){
// console.log('alltrue in if : ', alltrue);
this.props.handleMultiSelect();
}
if(this.props.checkBoxDefaultStatus){
this.props.handleMultiSelect();
}
}
render(){
//console.log('this.state.checkBoxClick :', this.state.checkBoxClick);
//console.log('this.props.checkBoxDefaultStatus :', this.props.checkBoxDefaultStatus);
return(
<div>
Child component check-box <br />
<input
type="checkbox"
checked={this.props.checkBoxDefaultStatus ? this.props.checkBoxDefaultStatus : this.state.checkBoxClick[1]}
onChange={(e) => {this.handleCheckBoxClick(1, e.target.checked)}}
/> Bar 1<br />
<input
type="checkbox"
checked={this.props.checkBoxDefaultStatus ? this.props.checkBoxDefaultStatus : this.state.checkBoxClick[2]}
onChange={(e) => {this.handleCheckBoxClick(2, e.target.checked)}}
/> Bar 2<br />
</div>
);
}
}
class A extends React.Component {
constructor() {
super();
this.state = {
checkBoxDefaultStatus: false
}
this.handleMultiSelect = this.handleMultiSelect.bind(this);
}
handleMultiSelect() {
//console.log('aaaa')
this.setState({
checkBoxDefaultStatus: !this.state.checkBoxDefaultStatus
})
}
render() {
//console.log('checkBoxDefaultStatus :', this.state.checkBoxDefaultStatus);
return (
<div>
<input type="checkbox" onClick={() => {this.handleMultiSelect()}} checked={this.state.checkBoxDefaultStatus}/>
Check all<br />
<B checkBoxDefaultStatus={this.state.checkBoxDefaultStatus}
handleMultiSelect={()=>{this.handleMultiSelect()}}
/>
</div>
);
}
}
ReactDOM.render( < A / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'></div>
Please using checked prop instead of defaultChecked
like this:
<input
type="checkbox"
inline={true}
checked={this.props.checkBoxDefaultStatus}
onChange={this.handleCheckBoxClick}
/>
Its very simple.
Use event.target.checked to know the checkbox status.
Example:
HTML: <input type="checkbox" onClick={(e) => OnCheckboxClick(e)}/>
JS:
const OnCheckboxClick= (e) => {
if(e.target.checked) {
//blah blah
}
else {
//blah blah
}
}
Related
I've got a few check boxes that, onClick, are pushed to state via .concat().
class App extends React.Component {
constructor() {
super()
this.state = {
selectedChoices: []
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(event) {
this.setState({
selectedChoices: [this.state.selectedChoices.concat(event.target.value)]
})
}
render() {
return(
<div>
<div onChange={this.handleClick}>
<input type="checkbox" name="choiceNumber" value="one" />
<input type="checkbox" name="choiceNumber" value="two" />
<input type="checkbox" name="choiceNumber" value="three" />
</div>
<p>{this.state.selectedChoices}</p>
</div>
)
}
}
Naturally, anytime the check box is clicked, regardless if it's .checked or not, that value is pushed to state.
I'm having some trouble finding the best way to go about this.
Above answer is correct but no need to use extra boolean "Flag".
use:
event.target.checked
Below snippet will work!
handleClick(event) {
if (!event.target.checked) {
this.setState({
selectedChoices: this.state.selectedChoices.filter(
(number) => number !== event.target.value
)
});
} else {
this.setState({
selectedChoices: this.state.selectedChoices.concat(event.target.value)
});
}
}
First, you don't have to wrap this.state.selectedChoices.concat(event.target.value) inside an array.
Second, you have to make condition if selected value has been added to selectedChoices you have to remove value from selectedChoices using filter. Else, you push value using concat just like you do.
import React from "react";
export default class App extends React.Component {
constructor() {
super();
this.state = {
selectedChoices: []
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
let flag = false;
for(const i in this.state.selectedChoices) {
if (this.state.selectedChoices[i] === event.target.value) {
flag = true;
break;
}
}
if(flag) {
this.setState({
selectedChoices: this.state.selectedChoices.filter(number => number !== event.target.value)
});
} else {
this.setState({
selectedChoices: this.state.selectedChoices.concat(event.target.value)
});
}
}
render() {
return (
<div>
<div onChange={this.handleClick}>
<input type="checkbox" name="choiceNumber" value="one" />
<input type="checkbox" name="choiceNumber" value="two" />
<input type="checkbox" name="choiceNumber" value="three" />
</div>
<p>{this.state.selectedChoices}</p>
</div>
);
}
}
here you can see how it run https://codesandbox.io/s/affectionate-diffie-0oi4t?file=/src/App.js
class Demo extends React.Component{
constructor (){
super();
this.state = {
list : ['car','map', 'house']
}
}
inputValue(e){
var x = e.target.value;
console.log(x)
}
addValue(){
this.state.list.push();
this.setState({list: this.state.list});
}
render(){
return(
<div>
<input onChange={this.inputValue} type="text"/>
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
)
}
}
ReactDOM.render(
<Demo/>,
document.getElementById('test')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="test"></div>
Using my code, how can i push the value from <input onChange={this.inputValue} type="text"/> in list : ['car','map', 'house']. I use for this addValue function, but i can't insert the x variable from inputValue function in push() from addValue function. How to do this using my code?
You need a state value for the text-input so that your addValue() function knows what to use when its time to add a new item. The text state will be updated with anything the user types.
Working demo: https://codesandbox.io/s/magical-feynman-fze1n
import React from "react";
class Demo extends React.Component {
constructor() {
super();
this.state = {
text: "",
list: ["car", "map", "house"]
};
}
inputValue(e) {
this.setState({
text: e.target.value
});
}
addValue() {
const text = this.state.text;
this.setState({ list: [...this.state.list, text] });
}
render() {
return (
<div>
<input onChange={this.inputValue.bind(this)} type="text" />
<ul>
{this.state.list.map(item => (
<li>{item}</li>
))}
</ul>
<button onClick={this.addValue.bind(this)}>Add Element</button>
</div>
);
}
}
export default Demo;
Also, refrain from doing direct state-mutations like this.state.list.push(blah). This is against React principles and can lead to unwanted visual side-effects. If you need to reference an existing state, try to create a copy of it instead. In the case for you list, we use the spread-operator to create a shallow-copy and then added the new item to the array..
Since React is all about small components and reusability consider breaking it up into two separate components... That way, if you need a form anywhere else you can reuse it...
Here is your Demo:
class Demo extends Component {
state = { list: ['car', 'map', 'house'] };
addItem = item => {
this.setState({ list: [item, ...this.state.list] });
};
render() {
return (
<div>
<Form addItem={this.addItem} />
{this.state.list.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
}
And here is the Form:
class Form extends Component {
state = { item: '' };
handleChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
this.props.addItem(this.state.item);
this.setState({ item: '' });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.item}
onChange={this.handleChange}
/>
</form>
);
}
}
Live Demo: https://stackblitz.com/edit/react-611uzp
i found a gist about how to pass state between two components.
Here the jsbin
But how about the multi state?
I want two input fields and show the entered text in other components when i edit it.
i tried edited like this
this.state = {
fieldVal: "" //first input state
otherFieldVal: "" //second
}
and
//input onChange
onUpdate = name => (event) => {
this.setState({ [name]: event.target.value });
};
with no luck.
How can i made it work on multi state for multi input fields ?
Don't need to keep state in both Child and parent. You can write your child component like below, and you can access tow states dynamically by using data-attirb or you can folloe #Isaac 's answer.Keep the state in Child and pass state to Parent or keep the event to Parent from Child.
export class Child extends React.Component {
update = (e) => {
this.props.onUpdate(e.target)
};
render() {
return (
<div>
<h4>Child</h4>
<input
type="text"
placeholder="type here"
onChange={this.update}
data-state = "fieldVal"
value={this.props.fieldVal}
/><br/><br/>
<input
type="text"
placeholder="type here"
onChange={this.update}
data-state = "otherFieldVal"
value={this.props.otherFieldVal}
/>
</div>
)
}
}
export class OtherChild extends React.Component {
render() {
return (
<div>
<h4>OtherChild</h4>
Value in OtherChild Props passedVal1: {this.props.passedVal1} <br/>
Value in OtherChild Props passedVal2: {this.props.passedVal2}
</div>
)
}
}
and in parent :
class App extends Component {
onUpdate = (data) => {
this.setState({
[data.dataset.state]: data.value
})
};
render() {
return (
<div>
<h2>Parent</h2>
Value in Parent Component State fieldVal: {this.state.fieldVal} <br/>
Value in Parent Component State otherFieldVal: {this.state.otherFieldVal}
<br/>
<Child onUpdate={this.onUpdate} fieldVal= {this.state.fieldVal} otherFieldVal ={this.state.otherFieldVal}/>
<br />
<OtherChild passedVal1={this.state.fieldVal} passedVal2={this.state.otherFieldVal}/>
</div>
);
}
}
demo
renderInput = (prop) => {
return (
<Input
onChange={(event) => {
this.setState({ [prop]: event.target.value });
}}
/>
)
}
render() {
<div>
{this.renderInput('name')}
{this.renderInput('age')}
</div>
}
We can set a renderInput method and render different input using parameter to achieve your objective
I have two hidden input fields that are being populated with the values from a prop when the node-fetch finishes and the values are being set without any issue, but I see the following warning.
Warning: A component is changing a controlled input of type hidden to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
While I understand the differences between controlled and uncontrolled, I can't seem to understand why my component would be creating a conflict between the two. Is there anything clear in my component code that could be causing this?
import React from 'react';
import isEqual from 'lodash/isEqual';
export default class DateFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
startDateValue: '',
endDateValue: ''
};
}
componentDidMount() {
this.setState({
startDateValue: this.props.startDateQuery,
endDateValue: this.props.endDateQuery
});
}
handleChange(input, value) {
this.setState({
[input]: value
})
}
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
render() {
return (
<div className="col-md-3">
<input type="hidden" name="_csrf" value={this.props.csrf} />
<div className="input-group blog-filter-date-range-picker">
<p>Blog Date Range:</p>
</div>
<div className="input-group blogFilterDatePicker">
<span className="input-group-addon"><i className="glyphicon glyphicon-calendar"></i></span>
<input type="text" name="blogDateRange" className="form-control blogFilterDatePicker" autoComplete="off" />
</div>
<input type="hidden" name="blogStartDate" className="form-control" value={this.state.startDateValue} onChange={e => this.handleChange('startDateValue', e.target.value)} />
<input type="hidden" name="blogEndDate" className="form-control" value={this.state.endDateValue} onChange={e => this.handleChange('endDateValue', e.target.value)} />
</div>
);
}
}
Parent Component:
import React from 'react';
import CatgoryFilter from './SearchFormFilters/CategoryFilter';
import DepartmentFilter from './SearchFormFilters/DepartmentFilter';
import TeamFilter from './SearchFormFilters/TeamFilter';
import TypeFilter from './SearchFormFilters/TypeFilter';
import DateFilter from './SearchFormFilters/DateFilter';
//Activity Feed - Search Form
export default class ActivityFeedSearchForm extends React.Component {
render() {
var clearFilters;
if(this.typeQuery || this.props.categoryQuery || this.props.departmentQuery || this.props.teamQuery || this.props.startDateQuery || this.props.endDateQuery || this.props.typeQuery){
clearFilters = Clear;
}
return (
<div className="row">
<div className="annotation-search-form col-md-10 col-md-offset-1">
<div clas="row">
<form action="/app" method="post" className="annotation-filter-fields">
<DateFilter csrf={this.props.csrf} startDateQuery={this.props.startDateQuery} endDateQuery={this.props.endDateQuery} />
<TypeFilter typeQuery={this.props.typeQuery} />
<CatgoryFilter category={this.props.category} categoryQuery={this.props.categoryQuery} />
<DepartmentFilter department={this.props.department} departmentQuery={this.props.departmentQuery} />
<TeamFilter team={this.props.team} teamQuery={this.props.teamQuery} />
<div className="col-md-1 annotation-filter-section filter-button-container">
<button type="submit" id="annotation-filter-submit">Filter</button>
{clearFilters}
</div>
</form>
</div>
</div>
</div>
)
}
}
Top-level Parent Component:
import React from 'react';
import fetch from 'node-fetch';
import ReactMarkdown from 'react-markdown';
import path from 'path';
import ActivityFeedSearchForm from './ActivityFeedSearchForm/ActivityFeedSearchForm';
import { API_ROOT } from '../config/api-config';
//GET /api/test and set to state
export default class ActivityFeed extends React.Component{
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || { team: [] };
}
fetchList() {
fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
.then(res => {
return res.json();
})
.then(data => {
this.setState({
startDateQuery: data.startDateQuery,
endDateQuery: data.endDateQuery,
});
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
this.fetchList();
}
render() {
return (
<div>
<Navigation notifications={this.state.notifications}/>
<ActivityFeedSearchForm csrf={this.state.csrf} category={this.state.category} categoryQuery={this.state.categoryQuery} department={this.state.department} departmentQuery={this.state.departmentQuery} team={this.state.team} teamQuery={this.state.teamQuery} typeQuery={this.state.typeQuery} startDateQuery={this.state.startDateQuery} endDateQuery={this.state.endDateQuery} />
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome} />
<LoadingIndicator loading={this.state.isLoading} />
<ActivityFeedLayout {...this.state} />
</div>
</div>
</div>
)
}
};
As you are dealing with only startDateValue and endDateValue in componentWillReceiveProps it’s good to compare them individually instead of whole props. Deep equality check is not happening for whole props and that’s why you get the warning
Change
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
To
componentWillReceiveProps(nextProps) {
if (this.props.startDateQuery != nextProps.startDateQuery && this.props.endDateQuery != nextProps.endDateQuery){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
Also you don’t need componentDidMount so remove that part in your code and update constructor code with below one
constructor(props) {
super(props);
this.state = {
startDateValue: this.props.startDateQuery ? this.props.startDateQuery: '',
endDateValue:this.props.endDateQuery ? this.props.endDateQuery: ''
};
}
}
And
Change
<input type="hidden" name="_csrf" value={this.props.csrf} />
To
<input type="hidden" name="_csrf" value={this.props.csrf ? this.props.csrf : "" />
And you are not binding handleChange so either bind it manually in constructor or change it to arrow function like below
Change
handleChange(input, value) {
this.setState({
[input]: value
})
}
To
handleChange = (input, value) => {
this.setState({
[input]: value
})
}
I am sending a callback function from a parent to a child component, and although the parent successfully updates it's state based on input provided to the child, it immediately reverts back to initial state, thus resulting in the browser briefly flashing the input that was provided and then displaying the initial state. What is the fix for this? Here is my code:
Parent:
class App extends Component {
constructor() {
super()
this.state = { item: '' }
this.getItem=this.getItem.bind(this);
}
getItem(val) {
this.setState({
item: val
})
console.log(this.state.item);
}
render() {
return (
<div className="App">
<Input getItem={this.getItem} />
<h2>{this.state.item}</h2>
</div>
);
}
}
Child:
class Input extends Component {
constructor(props) {
super(props)
this.state = { value: '' }
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
value: e.target.value
})
console.log(this.state.value)
}
handleSubmit(e) {
{this.props.getItem(this.state.value)};
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" name={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="+" />
</form>
</div>
)
}
}
I was able to solve this by using e.preventDefault() in the handleSubmit function.