How can I create the method when I click on one checkbox other checkbox unselceted and just can select one of them.
import React, { Component } from 'react';
export default class Tablerow extends Component {
constructor(props){
super(props);
let {listwebsites} = this.props;
listwebsites.checked = false;
this.state = {
fields : {
id: listwebsites.id
}
}
this.click = this.click.bind(this);
this.selectOnlyThis = this.selectOnlyThis.bind(this);
}
click(value){
this.props.handleChangess(this.state, value);
};
render() {
const {listwebsites} = this.props;
return (
<tr>
<td><input id={`checkbox_${listwebsites.id}`} value={listwebsites.checked} onChange={e => this.click(e.target.checked)} type="checkbox" name="record"/></td>
<td>{listwebsites.name}</td>
<td>{listwebsites.url}</td>
</tr>
)
}
}
Here's how you do it, in TableRow's parent which is App.js in this snippet, use selectedId state which store the id or TableRow's listwebsite's id if checked and will be null if not checked.
Inside your TableRow render, use disabled attr in your<input/> element. The disabled will check the selectedId props passed down from <App/>, if selectedId not null and selectedId value !== current <TableRow/> listwebsite's id, then disable the <input/>.
const listwebsitesData = [
{ id: 1, name: 'name-1', url: 'Url-1' },
{ id: 2, name: 'name-2', url: 'Url-2' },
{ id: 3, name: 'name-3', url: 'Url-3' }
]
class App extends React.Component {
constructor(props){
super(props);
this.state = {
selectedId: null,
}
this.handleChangess = this.handleChangess.bind(this);
}
handleChangess(id, value) {
this.setState({selectedId: value===true?id:null})
}
render(){
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{
listwebsitesData.map((data)=>{
return <Tablerow selectedId={this.state.selectedId} listwebsites={data} handleChangess={this.handleChangess} />
})
}
</div>
)
}
}
class Tablerow extends React.Component {
constructor(props) {
super(props);
let { listwebsites } = this.props;
listwebsites.checked = false;
this.state = {
fields: {
id: listwebsites.id
}
}
this.click = this.click.bind(this);
this.selectOnlyThis = this.selectOnlyThis.bind(this);
}
click(value) {
this.props.handleChangess(this.state.fields.id, value);
};
selectOnlyThis(){
}
render() {
const { listwebsites, selectedId } = this.props;
return (
<tr>
<td><input disabled={selectedId && selectedId!==listwebsites.id} id={`checkbox_${listwebsites.id}`} value={listwebsites.checked} onChange={e => this.click(e.target.checked)} type="checkbox" name="record" /></td>
<td>{listwebsites.name}</td>
<td>{listwebsites.url}</td>
</tr>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Basically, move the selection state one level up. You must be looping over Table row, in a Table component one level up. Put a state field there, say, 'selectedId', and pass it as prop to all the Table row components. Later, onChange will propagate from Table row with 'id' to Table onChangeHandler, for 'selectedId' update. In the render function for Table row, simply add say checked = id === selectedId thus, making only one of the Table rows selected at any given time. To make it more generic, you can later add say 'multiple' true/ false flag, where the component can switch between allowing multiple vs single checkbox selection.
Working example
https://codesandbox.io/s/zn9l7qpn83
By default, first one would be selected. As you select another one, it would deselect the other one; thus allowing only one to be selected at any given time.
Hope it helps!
You should add "checked={}" on checkbox and return true for the time you want it checked.
<input checked={selectedId && selectedId!==listwebsites.id} id={`checkbox_${listwebsites.id}`} value={listwebsites.checked} onChange={e => this.click(e.target.checked)} type="checkbox" name="record" />
This will check your checkbox when only this condition (selectedId && selectedId!==listwebsites.id)
Related
This is driving me crazy. I have no problem with select drop downs and text fields, but for some reason, I cannot get checkboxes working in a controlled fashion. That it is I want them to 'toggle' and listen to this event in a parent component.
I understand there is a 'checked' property for inputs of type checkbox. Selecting a checkbox gives it a value of 'on'. I take this 'on' value and convert it to true or false, and update the component accordingly.
For some reason, I cannot get this to work, either it is always selected, or it is never selected (if I switch the boolean switch).
export class ControlledCheckbox extends React.Component {
constructor(props) {
super(props);
this.state = {
select: false,
};
}
render() {
console.info("this.state.select - " + this.state.select);
let sel = false;
if (this.state.select !== "on") {
sel = true;
} else {
sel = false;
}
return (
<div>
<input
type="checkbox"
checked={sel}
onChange={this.handleChangeCheckbox}
/>
</div>
);
}
handleChangeCheckbox = (e) => {
console.info("here e.currentTarget.value " + e.currentTarget.value);
this.setState({
select: e.currentTarget.value,
});
//call passed through callback here
};
}
value serves a different purpose for checkbox inputs and you should not use it to define your state's value. You need to take a look at the e.currentTarget.checked instead.
export class ControlledCheckbox extends React.Component {
constructor(props) {
super(props);
this.state = {
select: false,
}
}
handleChangeCheckbox = e => {
this.setState({
select: e.currentTarget.checked // <--
})
}
render() {
return (
<div>
<input type="checkbox"
checked={this.state.select}
onChange={this.handleChangeCheckbox} />
</div>
);
}
}
If you are working with multiple inputs (not only checkboxes) you can follow the approach suggested by react docs, where you can cover multiple input types with only one setState. Keep in mind to define a name in this case, so you can separate your inputs:
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<div>
<input type="checkbox"
name="hasValue"
checked={this.state.select}
onChange={this.handleChangeCheckbox} />
</div>
);
}
Your question is about controlled inputs, however your checkbox isn't controlled yet. You rely on the value stored inside checkbox, not inside the state.
this.setState({
select: e.currentTarget.value, // here
});
Use a value that comes from the state instead.
this.setState((prevState) => ({
select: !prevState.select,
}));
Note: You can remove the conditions from the render, they are redundant.
I have created the following React component. It uses an input box to accept a user's answer to a riddle. As soon as the user's input matches the desired answer, the input box become read-only (a bit of a strange way to use them). It also has an "isHidden" prop to determine whether the riddle is rendered.
class Riddle extends React.Component {
constructor(props) {
super(props);
this.answer = props.answer.toUpperCase();
this.state = {
text: "",
isAnswered: false
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
let userInput = event.target.value.toUpperCase();
if (userInput == this.answer) {
this.setState({
text: userInput,
isAnswered: true
});
} else {
this.setState({
text: userInput,
isAnswered: false
});
}
}
render() {
if (this.props.isHidden) {
return <div></div>;
} else {
return (
<div>
<p>{this.props.prompt}</p>
<input type="text" value={this.state.text}
readOnly={this.state.isAnswered}></input>
</div>
);
}
}
}
Here it is in practice:
function App() {
return (
<div>
<Riddle prompt='The first three letters in the alphabet.' answer="abc" isHidden="false"/>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
What I would like to do is have a bunch of these riddles in sequence, but have riddles only be visible when the previous one was solved. The trouble is that I don't know how to cause the visibility update to happen.
I have read about lifting state from children to a parent component, and I've tried to see if I could create a RiddleSequence component with Riddles as its children, and have RiddleSequence manage visibility. My problem is that currently it is part of Riddle's state whether or not it's solved, and I don't know how RiddleSequence can read that information since child state should remain hidden. This seems like a reasonable way to encapsulate Riddle's functionality, but maybe I'm wrong given my goals.
I have also considered making Riddles be children of other riddles they depend on, since I can just pass state/props to children:
<Riddle prompt="first riddle"...>
<Riddle prompt="depends on first riddle"...>
<Riddle prompt="depends on second riddle"...>
</Riddle>
</Riddle>
</Riddle>
But if I have an app with 100 riddles, this seems to get ridiculous. This also reduces flexibility for a more expanded set of features (such as making one riddle depend on a group of 3 riddles).
How can I make the visibility of my Riddle components depend on the state of other riddles?
A simple solution would be to have a container component as you said:
class Riddle extends Component {
constructor(props) {
this.state = {
text: ''
}
this.answer = props.answer.toUpperCase()
}
handleChange = event => {
const userInput = event.target.value.toUpperCase()
const callback = userInput == this.answer ? this.props.onSolved : undefined
this.setState({ text: userInput }, callback)
}
render() {
const { text, isAnswered } = this.state
const { prompt } = this.props
if (this.props.isHidden) {
return null
}
return (
<div>
<p>{prompt}</p>
<input type="text" value={text} readOnly={isAnswered}></input>
</div>
)
}
}
and container should hold visibility like this:
class RiddleSequence extends Component {
state = {}
riddles = [
{
id: 1,
prompt: 'The first three letters in the alphabet.',
answer: 'abc',
prev: null
},
{
id: 2,
prompt: 'The last three letters in the alphabet.',
answer: 'xyz',
prev: 1
}
]
render() {
return (
<div>
{this.riddles.map(r => {
const { id, prev } = r
const visible = !prev || this.state[prev]
return (
<Riddle
key={id}
isHidden={!visible}
onSolved={() => this.setState({ [r.id]: true })}
{...r}
/>
)
})}
</div>
)
}
}
I have a form component that has a state containing an array of items.
I am having a hard time trying to update the state of the form when one of the item inputs gets updated.
At first I was creating a state on the items themselves and updating the values using the following code:
class ItemRow extends Component{
constructor(props){
super(props)
this.state = this.props.item;
}
updateItem(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
.....
render(){
return (
<FormControl
type="text"
name="name"
value={this.state.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.state.price}
onChange={this.updateItem}
/>
.....
)
}
}
This worked fine for updating the value of the of the inputs, however the state was local to the item and not reflected or accessible by the form
I am trying to figure out how to keep the state in the form and have the item update the state of the form
I think this is the right approach but I can't figure out how to get it to work.
At this point I have something similar the following:
class Form extends Component{
this.state = {
items: [
{ name: 'soup', price: 7, quantity: 1 }
{ name: 'salad', price: 5, quantity: 2 }
]
}
updateItem(e) {
// Not sure how to handle updating
}
removeItem(item) {
let items = this.state.items;
items.splice(items.indexOf(item), 1);
this.setState({items: items})
}
render(){
return(
<ItemTable items={this.state.items} updateItem={this.updateItem} removeItem={this.removeItem} />
)
}
}
ItemTable:
class ItemTable extends Component {
removeItem(item){
this.props.removeItem(item)
}
render(){
let items = [];
this.props.items.forEach((item) => {
items.push(<ItemRow item={item} key={item.id} removeItem={this.removeItem.bind(this,item)} updateItem={this.props.updateItem}/>);
});
return(
{items}
)
}
}
ItemRow:
class ItemRow extends Component {
removeItem(item){
this.props.removeItem(item)
}
render() {
return (
<FormControl
type="text"
name="name"
value={this.props.item.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="quantity"
value={this.props.item.quantity}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.props.item.price}
onChange={this.updateItem}
/>
<Button bsStyle="warning" onClick={this.removeItem}><Glyphicon glyph="trash"/></Button>
)
}
}
You're very close to the solution.
If you need to have a state shared between components, you should have it in the most parent component that should be aware of the state (in your case the Form component).
You pass down as props the method "updateItem" from the Form to the ItemTable and then ItemRow (like you're doing)
At this stage, inside the ItemRow you can use the method by calling 'this.props.updateItem' and you can run the function defined in Form, passing some parameters, if you need to.
I'm teaching myself react with a super simple app that asks the user to type a word presented in the UI. If user enters it correctly, the app shows another word, and so on.
I've got it almost working, except for one thing: after a word is entered correctly, I need to clear the input element. I've seen several answers here about how an input element can clear itself, but I need to clear it from the component that contains it, because that's where the input is checked...
// the app
class AppComponent extends React.Component {
constructor() {
super();
this.state = {
words: ['alpha', 'bravo', 'charlie'],
index: 0
};
}
renderWordsource() {
const word = this.state.words[this.state.index];
return <WordsourceComponent value={ word } />;
}
renderWordinput() {
return <WordinputComponent id={1} onChange={ this.onChange.bind(this) }/>;
}
onChange(id, value) {
const word = this.state.words[this.state.index];
if (word == value) {
alert('yes');
var nextIndex = (this.state.index == this.state.words.count-1)? 0 : this.state.index+1;
this.setState({ words:this.state.words, index:nextIndex });
}
}
render() {
return (
<div className="index">
<div>{this.renderWordsource()}</div>
<div>{this.renderWordinput()}</div>
</div>
);
}
}
// the input component
class WordinputComponent extends React.Component {
constructor(props) {
this.state = { text:''}
}
handleChange(event) {
var text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="wordinput-component">
<input type="text" onChange={this.handleChange.bind(this)} />
</div>
);
}
}
See where it says alert('yes')? That's where I think I should clear the value, but that doesn't make any sense because it's a parameter, not really the state of the component. Should I have the component pass itself to the change function? Maybe then I could alter it's state, but that sounds like a bad idea design-wise.
The 2 common ways of doing this is controlling the value through state in the parent or using a ref to clear the value. Added examples of both
The first one is using a ref and putting a function in the child component to clear
The second one is using state of the parent component and a controlled input field to clear it
class ParentComponent1 extends React.Component {
state = {
input2Value: ''
}
clearInput1() {
this.input1.clear();
}
clearInput2() {
this.setState({
input2Value: ''
});
}
handleInput2Change(evt) {
this.setState({
input2Value: evt.target.value
});
}
render() {
return (
<div>
<ChildComponent1 ref={input1 => this.input1 = input1}/>
<button onClick={this.clearInput1.bind(this)}>Clear</button>
<ChildComponent2 value={this.state.input2Value} onChange={this.handleInput2Change.bind(this)}/>
<button onClick={this.clearInput2.bind(this)}>Clear</button>
</div>
);
}
}
class ChildComponent1 extends React.Component {
clear() {
this.input.value = '';
}
render() {
return (
<input ref={input => this.input = input} />
);
}
}
class ChildComponent2 extends React.Component {
render() {
return (
<input value={this.props.value} onChange={this.props.onChange} />
);
}
}
ReactDOM.render(<ParentComponent1 />, document.body);
<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>
I had a similar issue: I wanted to clear a form which contained multiple fields.
While the two solutions by #noveyak are working fine, I want to share a different idea, which gives me the ability to partition the responsibility between parent and child: parent knows when to clear the form, and the items know how to react to that, without using refs.
The idea is to use a revision counter which gets incremented each time Clear is pressed and to react to changes of this counter in children.
In the example below there are three quite simple children reacting to the Clear button.
class ParentComponent extends React.Component {
state = {revision: 0}
clearInput = () => {
this.setState((prev) => ({revision: prev.revision+1}))
}
render() {
return (
<div>
<ChildComponent revision={this.state.revision}/>
<ChildComponent revision={this.state.revision}/>
<ChildComponent revision={this.state.revision}/>
<button onClick={this.clearInput.bind(this)}>Clear</button>
</div>
);
}
}
class ChildComponent extends React.Component {
state = {value: ''}
componentWillReceiveProps(nextProps){
if(this.props.revision != nextProps.revision){
this.setState({value : ''});
}
}
saveValue = (event) => {
this.setState({value: event.target.value})
}
render() {
return (
<input value={this.state.value} onChange={this.saveValue} />
);
}
}
ReactDOM.render(<ParentComponent />, document.body);
<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>
EDIT:
I've just stumbled upon this beautifully simple solution with key which is somewhat similar in spirit (you can pass parents's revision as child's key)
Very very very simple solution to clear form is add unique key in div under which you want to render form from your child component key={new Date().getTime()}:
render(){
return(
<div className="form_first_step fields_black" key={new Date().getTime()}>
<Form
className="first_step">
// form fields coming from child component
<AddressInfo />
</div>
</Form>
</div>
)
}
I have created dynamically generate input text-fields but unable to find a way to read and get the values and stored it to an array. please find the code below
i have separate component for add new input field rows called IncrementTableRow
import React, {PropTypes} from 'react';
class IncrementTableRow extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<tr>
<th scope="row">{this.props.index}</th>
<td><input type="text" className="form-control" ref={"firstValue"+this.props.index} placeholder=""/></td>
<td><input type="text" className="form-control" ref={"secondValue"+this.props.index} placeholder=""/></td>
</tr>
);
}
}
export default IncrementTableRow;
also, i have main component to call IncrementTableRow below is the calling line.
export default class SuggestInterestProductDetails extends Component {
constructor(props) {
super(props);
this.state = {
rows: []
};
this.AddRow = this.AddRow.bind(this);
}
AddRow() {
this.setState({
rows: [{val: 5}, ...this.state.rows]
});
}
render() {
let rows = this.state.rows.map(row => {
return <Row />
});
return (
<div>
<button onClick={this.AddRow}>Add Row</button>
<table>
{rows}
</table>
</div>
);
}
}
i need to read each and every generated text field values and stored it to an array
your code example seems incomplete - you dont even add the values to your rows
so here only a short answer:
check react refs
https://facebook.github.io/react/docs/more-about-refs.html
you can add a ref to each row in your
let rows = this.state.rows.map(row => {
return <Row />
});
maybe an even better solution would be to add an onChange listener to your rows and update the state of your parrent component
let rows = this.state.rows.map((row,i) => {
return <Row ref={'row-'+i} onChange={(event) => this.myListener(event,i)} />
});