I want to so some customization with checkbox, it can look like this:
so I wrap my custom checkbox into a React Component:
require('../../less/ck-checkbox.less');
var React = require('react');
var CkCheckbox = React.createClass({
propTypes: {
name: React.PropTypes.string,
text: React.PropTypes.string,
defaultChecked: React.PropTypes.bool,
onChange: React.PropTypes.func
},
getDefaultProps: function() {
return {
name: 'checkbox',
text: '',
defaultChecked: false,
onChange: function () {}
};
},
render: function() {
return (
<div className="ck-checkbox">
<label>
<input
type="checkbox"
name={this.props.name}
ref="c"
defaultChecked={this.props.defaultChecked}
onChange={this.props.onChange.bind(this, this.refs.c.checked)}
/>
<span className="icons">
<span className="icon-checked-o icon-true"></span>
<span className="icon-circle-o icon-false"></span>
</span>
{this.props.text.length > 0 ?
<span className="ck-checkbox-text">{this.props.text}</span> : null
}
</label>
</div>
);
}
});
module.exports = CkCheckbox;
and my container is like this:
var React = require('react');
var CkCheckbox = require('./CkCheckbox.js');
var Test = React.createClass({
render: function() {
return (
<div>
<CkCheckbox onChange={this._handleChange}/>
</div>
);
},
_handleChange: function(checked, e) {
console.log(checked)
}
});
module.exports = Test;
you can see that, I tried to bind this.refs.c.checked in the onChange callback, but it doesn't work.
so, how can I get the checked state of my custom checkbox in Test component in the _handleChange function?
In this case you don't need use refs because you can get checked property from e argument
// CkCheckbox component
<input type="checkbox"
name={this.props.name}
defaultChecked={this.props.defaultChecked}
onChange={ this.props.onChange } />
// Test component
_handleChange: function(e) {
var checked = e.target.checked;
console.log(checked)
}
Example
or if you want pass only checked property you can do it like this
// CkCheckbox component
handleChange: function (e) {
this.props.onChange(e.target.checked);
},
<input type="checkbox"
name={this.props.name}
defaultChecked={this.props.defaultChecked}
onChange={ this.handleChange } />
// in Test component
_handleChange: function(checked) {
console.log(checked)
}
Example
This is a simple example you can use to get the custom value or the value of your checked box, and also to check if the box is checked.
export default class SearchBoxContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
boxAll: false
};
}
handleChange = event => {
this.setState({ boxAll: event.target.checked }, () => {
console.log("This returned true or false", this.state.boxAll);
});
};
render() {
return (
<div className="search-container">
<SearchBox />
<div className="filter-country">
<h1>Filter</h1>
<div className="filter-country-container">
<div>
<input
type="checkbox"
id="checkBoxUS"
name="US"
value="US"
onChange={this.handleChange}
/>
<label htmlFor="US">All Jobs</label>
</div>
</div>
</div>
</div>
);
}
}
It is important to know that in this example, I used the "setState()" callback just for demonstration purposes, but you can pass the value to other components by props or wherever method you prefer. Also, when the checkbox is checked the value will return "true"
Related
I have a component InputArea with state = {input: ''}
Then I map several of these components in a container and write them in state = {inputAreas: []}
Now, how can I get inputs in the container? Logging this.state.inputAreas[0] returns object like this:
{$$typeof: Symbol(react.element), type: ƒ, key: "1", ref: null, props:
{…}, …}
In elements it shows like this:
<input type="text" class="form-control" name="input" value="abc">
Using this.state.prefooterArea[0].value gives undefined.
I also tried passing input from component to container as props, but it says getInput is not a function. From what I understood it has something to do with the fact I used map in the container. I can't use redux in this project.
Code of component
class PrefooterAreaInput extends Component {
state = {
input: ''
}
textChangedHandler = (event) => {
let newState = {};
newState[event.target.name] = event.target.value;
this.setState(newState);
}
render() {
return (
<div>
<input
className="form-control"
type="text"
name="input"
value = {this.state.input}
onChange={this.textChangedHandler}
/>
</div>
)
}
}
Code of container
class DescriptionFrame extends Component {
state = {,
prefooterArea: [<PrefooterAreaInput key={1}/>]
};
addFooterInputHandler = event => {
event.preventDefault();
if (this.state.prefooterArea.length < prefooterInputFieldsMax) {
var newPrefooterArea = this.state.prefooterArea.map(
inputField => inputField
);
newPrefooterArea.push(
<PrefooterAreaInput key={this.state.prefooterArea.length + 1} />
);
this.setState({ prefooterArea: newPrefooterArea });
}
};
removeFooterInputHandler = event => {
event.preventDefault();
if (this.state.prefooterArea.length > 1) {
var newPrefooterArea = this.state.prefooterArea.map(
inputField => inputField
);
newPrefooterArea.splice(newPrefooterArea.length - 1);
this.setState({ prefooterArea: newPrefooterArea });
}
render() {
// want to get this.state.prefooterArea[0]'s value
return (
<div>
{this.state.prefooterArea}
<a
className="nav-link"
href=""
onClick={this.addFooterInputHandler}
>
Add More
</a>
<a
className="nav-link"
href=""
onClick={this.removeFooterInputHandler}
>
Remove Last
</a>
</div>
);
}
}
Figured it out. This caused problem.
prefooterArea: [<PrefooterAreaInput key={1}/>]
I should have added that initial PrefooterAreaInput with lifecycle method instead. With that I was able to pass state just fine.
Are you trying to achieve something like this ?
child component :
export default class InputBox extends React.Component {
render() {
return (
<input onChange={event => this.props.onChange(event.target.value)} />
);
}}
parent component :
import InputBox from './InputBox';
class FilterBar extends React.Component {
constructor(props) {
super(props);
this.state = {
inputs: "" //get input value from state this input
};
this.updateFilters = this.updateFilters.bind(this);
}
updateFilters(i) {
this.setState({ inputs: i }); // this will print whatever input you type
}
render() {
return (
<div>
<InputBox onChange={(i) => this.updateFilters(i)} />
</div>
);
}
}
I have multiple input fields and 1 react-select dropdown field. I created a method in my parent component that sets the state with the values from the input, passes it down to the child which should call the method. My problem is that react-select doesn't take the value but an object like this:
{value: 'xy', name:'x', label: 'y'}
so normally my function in my onChange event handler would look like this (when passing multiple values):
in parent:
testing(e) {
this.setState({
[e.target.name]: e.target.value
})
}
in child:
<input type="text" name="maxfare" onChange={this.onChange}/>
...
onChange(e){
var value = [e.target.name] = e.target.value;
this.props.onChange(value);
}
...
However, while my input fields take:
e.target.value
my select dropdown takes entire 'e' - not e.target.value. I tried to pass my onChange function in child component 2 arguments, calling my method in parent with 2, but that doesn't to work. Any help would be great! My code is below (the relevant parts- if I forgot something that you think is important, please let me know). Ps. I thought about having 2 onChange functions, passing once my value for select dropdown and a second one doing the rest, but then I would need to pass 2 onChange methods to the child and I believe thats not possible in react?! Thanks!!:
Parent:
...
onChangeT(selectValue, value) {
this.setState({
origin: selectValue,
maxfare: value
...
})
}
render(){
....
<Parent cities={this.state.citiesToSelect} origin={this.state.origin} maxfare={this.state.maxfare} onChange={this.onChangeT}/>
...
}
Child:
....
onChangeC(e){
var value = [e.target.name] = e.target.value;
this.props.onChange(e, value);
console.log("name", name)
}
....
<Select
onChange={this.onChangeC}
labelKey='name'
value={this.props.origin}
options={this.props.cities}
/>
<input type="text" name="maxfare" onChange={this.onChangeC}/>
We want to be able to do this in the parent
onChange = (name, value) => {
this.setState({[name]: value});
}
We fix the "wiring" of the children onChange to do exactly that, raise an onChange with a name and a value. Wrap react-select and provide a consistent interface to the parent.
Form example
import * as React from 'react';
import Input from './Input';
import Select from './Select';
export default class Form extends React.Component {
state = {
input: '',
select: '',
options: ['A', 'B', 'C']
};
onChange = (name: string, value: string) => {
this.setState({[name]: value});
}
render() {
return (
<form>
<Input
label="Surname"
name={'input'}
value={this.state.input}
onChange={this.onChange}
/>
<Select
label="Grade"
name={'select'}
value={this.state.select}
options={this.state.options}
onChange={this.onChange}
/>
</form>
);
}
}
Input example
import * as React from 'react';
export default class Input extends React.Component {
onChange = (e) => {
const {onChange, name} = this.props;
if (onChange) {
onChange(name, e.currentTarget.value);
}
}
render() {
return (
<div>
<label>{this.props.label}</label>
<input
type="text"
name={this.props.name}
value={this.props.value}
onChange={this.onChange}
/>
</div>
);
}
}
And a DOM native <Select /> example
import * as React from 'react';
export default class Select extends React.Component {
onChange = (e) => {
const {onChange, name} = this.props;
if (onChange) {
onChange(name, e.currentTarget.value);
}
}
render() {
return (
<div>
<label>{this.props.label}</label>
<select
name={this.props.name}
value={this.props.value}
onChange={this.onChange}
>
{this.props.options.map(o => <option key={o}>{o}</option>)}
</select>
</div>
);
}
}
The fact that react-select doesn't return a native event nor a similar object shape of a native event, is forcing you to normalize the shape of the object that returned from it. You can do that by wrapping the Select component of react-select with your own component and returning a custom object for your use-case.
In this example we are trying to normalize the behavior of our onChange event both for inputs and Select. We will first check if the object that returned is having a target key, if it does we know that this is a native event that we are handling and we will set the state according to the name of the input and its value (exactly how you did it in your example).
If we don't have a target key, then we may handle a different kind of event.
We will check if we get a selectedValue key (just a convention between yourself, you can change the key as you like), then we will set the state by its name and selectedValue that we received.
This will only work if you will pass the name upwards of course.
So the object that you need to return from the custom Select component should look something like this:
{name: this.props.name, selectedValue }
// where selectedValue is the object received from the real Select component
Here is a running example:
const options = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
]
const moreOptions = [
{ value: 'mike', label: 'johnson' },
{ value: 'lynda', label: 'bog' },
]
class MySelect extends React.Component {
handleChange = selectedValue => {
const { name, onChange } = this.props;
onChange({ name, selectedValue });
}
render() {
const { options, value, ...rest } = this.props;
return (
<Select
{...rest}
value={value}
onChange={this.handleChange}
options={options}
/>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
option1: '',
option2: '',
value1: 1,
value2: '',
value3: 3,
}
}
handleChange = e => {
let nextState;
if (e.target) {
const { name, value } = e.target;
nextState = { [name]: value };
} else if (e.selectedValue) {
const { name, selectedValue } = e;
nextState = { [name]: selectedValue };
}
this.setState(nextState);
}
render() {
const { value1, value2, value3, option1, option2 } = this.state;
return (
<div>
<MySelect
value={option1.value}
onChange={this.handleChange}
options={options}
name="option1"
/>
<div>
<span>input1 </span>
<input value={value1} name="value1" onChange={this.handleChange} />
</div>
<div>
<span>input2 </span>
<input value={value2} name="value2" onChange={this.handleChange} />
</div>
<div>
<span>input3 </span>
<input value={value3} name="value3" onChange={this.handleChange} />
</div>
<MySelect
value={option2.value}
onChange={this.handleChange}
options={moreOptions}
name="option2"
/>
</div>
);
}
}
ReactDOM.render(<App />, 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>
<script src="https://unpkg.com/prop-types#15.5.10/prop-types.js"></script>
<script src="https://unpkg.com/classnames#2.2.5/index.js"></script>
<script src="https://unpkg.com/react-input-autosize#2.0.0/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/react-select/dist/react-select.js"></script>
<link rel="stylesheet" href="https://unpkg.com/react-select/dist/react-select.css">
<div id="root"></div>
It there a best practice to simplify something like the following in React?
getInitialState: function () {
return {
checkbox1: false,
checkbox2: false,
checkbox3: false,
...
};
},
selectCheckbox1: function () {
this.setState({
checkbox1: !this.state.checkbox1
});
},
selectCheckbox2: function () {
this.setState({
checkbox2: !this.state.checkbox2
});
},
selectCheckbox3: function () {
this.setState({
checkbox3: !this.state.checkbox3
});
},
...
render: function () {
return (
<div>
<input type="checkbox" checked={this.state.checkbox1} onChange={this.selectCheckbox1} />
<input type="checkbox" checked={this.state.checkbox2} onChange={this.selectCheckbox2} />
<input type="checkbox" checked={this.state.checkbox3} onChange={this.selectCheckbox3} />
...
</div>
);
}
For example, I can use an array for the state instead of individual fields and create a generic function that takes an index parameter to distinguish which checkbox to update:
const Checkboxes = React.createClass({
getInitialState: function () {
return {
checkboxes: [false, false, false, ...]
};
},
selectCheckbox: function (index) {
let checkboxes = this.state.checkboxes.slice();
checkboxes[index] = !checkboxes[index];
this.setState({
checkboxes: checkboxes
});
},
render: function () {
return (
<div>
<input type="checkbox" checked={this.state.checkboxes[0]} onChange={() => this.selectCheckbox(0)} />
<input type="checkbox" checked={this.state.checkboxes[1]} onChange={() => this.selectCheckbox(1)} />
<input type="checkbox" checked={this.state.checkboxes[2]} onChange={() => this.selectCheckbox(2)} />
...
</div>
);
}
});
I am new to React and JavaScript so I don't know exactly the tradeoffs happening behind the scenes here. Is the second an improvement over the first? Which is preferred and why?
Second one is definitely better approach then the first one. You can safely go with that.
Apart from that, you can also render the check box without repeating them each time in render function by using array map.
render: function () {
return (
<div>
{this.state.checkboxes.map((c, i) => {
return (
<input key={i} type="checkbox" checked={c} onChange={() => this.selectCheckbox(i)} />
);
})}
</div>
);
}
I would do something like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
checkboxes: {
cheese: false,
lettuce: false,
tomatoe: false
}
};
}
handleChange = e => {
const checkboxId = e.target.id;
this.setState({
checkboxes: {
...this.state.checkboxes,
[checkboxId]: !this.state.checkboxes[checkboxId]
}
});
};
render() {
return (
<div>
{Object.entries(this.state.checkboxes).map(([key, val]) => {
return (
<div key={key}>
<input
type="checkbox"
checked={val}
id={key}
onChange={this.handleChange}
/>
<label>
{key}
</label>
</div>
);
})}
</div>
);
}
}
This makes things more explicit and easier to follow and has the added benefit of not creating a new anonymous function each time you render.
I prefer having the checkboxes named (in an object, not an array), like in your first example. But that could vary by use case. Having them unnamed as an array does, as Prakash-sharma points out, provide the benefit of being able to map over them.
This is how I would do it to reduce the duplication of the callback function declarations (without storing the checkbox values in an array):
const Checkboxes = React.createClass({
getInitialState: function () {
return {
checkbox1: false,
checkbox2: false,
checkbox3: false
};
},
selectCheckbox: function (checkboxNr) {
// return a callback with the checkboxNr set
return () => {
this.setState({
[checkboxNr]: !this.state[checkboxNr]
});
}
},
render: function () {
const {checkboxes1, checkboxes2, checkboxes3} = this.state;
return (
<div>
<input type="checkbox" checked={checkboxes1} onChange={ this.selectCheckbox("checkbox1") } />
<input type="checkbox" checked={checkboxes2} onChange={ this.selectCheckbox("checkbox2") } />
<input type="checkbox" checked={checkboxes3} onChange={ this.selectCheckbox("checkbox3") } />
</div>
);
}
});
This is for a react JS project (jsfiddle). The textbox should update with the true/false checked value of the checkbox, but it does not do so. Can someone explain why?
var AutoGenerateCheckbox = React.createClass ({
getInitialState: function() {
return {checked: false};
},
update() {
this.state.checked = !this.state.checked;
alert(this.state.checked);
this.props.onUpdate(this.state.checked);
},
render() {
return (
<input type="checkbox" checked={this.state.checked} onChange={this.update} />
);
}
});
var TBox = React.createClass({displayName: 'TextBox',
render: function() {
return (
<div>
Checkbox value: {this.props.data}
</div>
);
}
});
var KApp = React.createClass({
getInitialState: function() {
return {autoChecked: false};
},
handleAutogenChange: function(val) {
alert('handleAutogenChange:' + val);
this.setState({autoChecked : val});
},
render: function() {
return (
<div>
<AutoGenerateCheckbox onUpdate={this.handleAutogenChange}/>
<TBox data={this.state.autoChecked}/>
</div>
);
}
});
ReactDOM.render(
<KApp />,
document.getElementById('content')
);
The reason you don't see anything printed out is because you are trying to print a boolean value here
<div>
Checkbox value: {this.props.data}
</div>
try
<div>
Checkbox value: {this.props.data.toString()}
</div>
instead.
As an extra tip, you don't really need to hold the state of the checkbox in both its own state and its parent component's state. You really only need to have it in the parent component's state.
See the fiddle I made.
React is not determining the Boolean value to be printable information, try this instead:
<div>
Checkbox value: {this.props.data.toString()}
</div>
Just started my first app in React and I want to know if there is a React way to work with Radio Buttons, I have a form with 4 radio buttons, I need to take 2 of the options selected and send that info to a backend.
class RadioBtns extends Component {
constructor (props) {
super(props);
this.state = {
greet : '',
hello : '',
};
}
render () {
return (
<div>
<div>
<form>
<input type="radio" value="first" name="greet" onChange={this._onChangeGreet}/> Option 1
<input type="radio" value="second" name="greet" onChange={this._onChangeGreet}/> Option 2
<input type="radio" value="three" name="hello" onChange={this._onChangeHello}/> Option 3
<input type="radio" value="four" name="hello" onChange={this._onChangeHello}/> Option 4
</form>
<hr />
<button type="submit" onClick={this._submitSettings}>YES!</button>
</div>
</div>
);
}
_onChangeGreet = ({ target }) => {
this.setState({
greet : target.value,
});
}
_onChangeHello = ({ target }) => {
this.setState({
hello : target.value,
});
}
_submitSettings = () => {
console.log('submit');
}
}
export default RadioBtns;
how do I send this states with the values to the stores ?
and here I have the action
#createActions(flux)
class RadioBtnsActions {
constructor () {
this.generateActions('optionSelected');
}
}
export default RadioBtnsActions;
and in the Store
import flux from 'flux';
import RadioBtnsActions from 'actions/RadioBtnsActions';
#createStore(flux)
class RadioBtnsStore {
constructor () {
this.state = {
radioSelected : false,
};
}
#bind(RadioBtnsActions.optionSelected)
optionSelected (option) {
this.setState({
radioSelected : option,
});
}
}
export default RadioBtnsStore;
Here's what we did in our project (simplified, use your imagination):
First you create a RadioButton component that renders the actual input:
render(){
<div>
<input id={this.props.id} type="radio"
name={this.props.name} value={this.props.value}
checked={this.props.checked} onChange={this.onChange}/>
<label htmlFor={this.props.id}>{this.props.label}</label>
</div>
},
onChange: function(ev){
this.props.onChange(ev.target.checked, this.props.value);
}
Then you use that to implement a RadioButtonGroup component:
render: function(){
var name = this.name, value = this.props.value, onChange = this.onSingleRadioChange;
var options = _.map(this.props.options, function(option){
var id = name + '-' + option.value;
return <RadioButton key={option.value} id={id} name={name} value={option.value} label={option.label} checked={option.value == value} onChange={onChange} />
});
return <div>{options}</div>
},
onSingleRadioChange: function(checked, value){
if(checked)
this.props.onChange(value);
}
You can use it like this:
<RadioButtonGroup name='greet' options={[{value: 'first', label: 'First'}, {value: 'second', label: 'Second'}]} onChange={val => { Actions.radioGroupChanged('greet', val);}} />
Where Actions.radioGroupChanged is the action that your store is listening on.
Don't forget to use labels for better UX.
Edit: here's a rough draft of the store, although we use Reflux, so it's a different API that what you have:
var store = Reflux.createStore({
radioGroups: {greet: 'first', hello: 'three'}, //state of the radio button groups lives here
init(){
this.listenTo(Actions.radioGroupChanged, this.onRadioGroupChanged);
},
onRadioGroupChanged(group, value){
this.radioGroups[group] = value;
this.trigger(); //this notifies the component that the store changed;
}
});
The component then listens to the store and updates its own state:
componentDidMount(){
this.listenTo(store, () => { this.setState({radios: store.groups}) });
}
render(){
return <RadioButtonGroup name='greet' value={this.state.radios.greet} .../>
}