I have this component:
import React from 'react';
export default class AddItem extends React.Component {
add() {
this.props.onButtonClick(this.input.value);
this.input.value = '';
}
render() {
return (
<div className="add-item">
<input type="text" className="add-item__input" ref={(input) => this.input = input} placeholder={this.props.placeholder} />
<button disabled={!this.input.value} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
</div>
);
}
}
I want the button to be disabled when input value is empty. But the code above doesn't work. It says:
add-item.component.js:78 Uncaught TypeError: Cannot read property 'value' of undefined
pointing to disabled={!this.input.value}. What can I be doing wrong here? I'm guessing that perhaps ref isn't created yet when render method is executed. If, so what is the workararound?
Using refs is not best practice because it reads the DOM directly, it's better to use React's state instead. Also, your button doesn't change because the component is not re-rendered and stays in its initial state.
You can use setState together with an onChange event listener to render the component again every time the input field changes:
// Input field listens to change, updates React's state and re-renders the component.
<input onChange={e => this.setState({ value: e.target.value })} value={this.state.value} />
// Button is disabled when input state is empty.
<button disabled={!this.state.value} />
Here's a working example:
class AddItem extends React.Component {
constructor() {
super();
this.state = { value: '' };
this.onChange = this.onChange.bind(this);
this.add = this.add.bind(this);
}
add() {
this.props.onButtonClick(this.state.value);
this.setState({ value: '' });
}
onChange(e) {
this.setState({ value: e.target.value });
}
render() {
return (
<div className="add-item">
<input
type="text"
className="add-item__input"
value={this.state.value}
onChange={this.onChange}
placeholder={this.props.placeholder}
/>
<button
disabled={!this.state.value}
className="add-item__button"
onClick={this.add}
>
Add
</button>
</div>
);
}
}
ReactDOM.render(
<AddItem placeholder="Value" onButtonClick={v => console.log(v)} />,
document.getElementById('View')
);
<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='View'></div>
In HTML,
<button disabled/>
<button disabled="true">
<button disabled="false">
<button disabled="21">
All of them boils down to disabled="true" that is because it returns true for a non-empty string.
Hence, in order to return false, pass a empty string in a conditional statement like this.input.value ? "true" : "".
render() {
return (
<div className="add-item">
<input
type="text"
className="add-item__input"
ref={(input) => this.input = input}
placeholder={this.props.placeholder}
/>
<button
disabled={this.input.value ? "true" : ""}
className="add-item__button"
onClick={this.add.bind(this)}
>
Add
</button>
</div>
);
}
Here is a functional component variety using react hooks.
The example code I provided should be generic enough for modification with the specific use-case or for anyone searching "How to disable a button in React" who landed here.
import React, { useState } from "react";
const YourComponent = () => {
const [isDisabled, setDisabled] = useState(false);
const handleSubmit = () => {
console.log('Your button was clicked and is now disabled');
setDisabled(true);
}
return (
<button type="button" onClick={handleSubmit} disabled={isDisabled}>
Submit
</button>
);
}
export default YourComponent;
There are few typical methods how we control components render in React.
But, I haven't used any of these in here, I just used the ref's to namespace underlying children to the component.
class AddItem extends React.Component {
change(e) {
if ("" != e.target.value) {
this.button.disabled = false;
} else {
this.button.disabled = true;
}
}
add(e) {
console.log(this.input.value);
this.input.value = '';
this.button.disabled = true;
}
render() {
return (
<div className="add-item">
<input type="text" className = "add-item__input" ref = {(input) => this.input=input} onChange = {this.change.bind(this)} />
<button className="add-item__button"
onClick= {this.add.bind(this)}
ref={(button) => this.button=button}>Add
</button>
</div>
);
}
}
ReactDOM.render(<AddItem / > , 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>
You shouldn't be setting the value of the input through refs.
Take a look at the documentation for controlled form components here - https://facebook.github.io/react/docs/forms.html#controlled-components
In a nutshell
<input value={this.state.value} onChange={(e) => this.setState({value: e.target.value})} />
Then you will be able to control the disabled state by using disabled={!this.state.value}
very simple solution for this is by using useRef hook
const buttonRef = useRef();
const disableButton = () =>{
buttonRef.current.disabled = true; // this disables the button
}
<button
className="btn btn-primary mt-2"
ref={buttonRef}
onClick={disableButton}
>
Add
</button>
Similarly you can enable the button by using buttonRef.current.disabled = false
this.input is undefined until the ref callback is called. Try setting this.input to some initial value in your constructor.
From the React docs on refs, emphasis mine:
the callback will be executed immediately after the component is mounted or unmounted
I have had a similar problem, turns out we don't need hooks to do these, we can make an conditional render and it will still work fine.
<Button
type="submit"
disabled={
name === "" || email === "" || password === ""
}
fullWidth
variant="contained"
color="primary"
className={classes.submit}>
SignUP
</Button>
Related
I've created multiple checkboxes in my small project. I'm facing a problem in retrieving the info of the checkbox while giving a checkmark. if I give a checkmark on toys then the value should be toys or if I give checkmark on both toys and lights i should get both toys and lights in the console.
Here is code:
import React from "react";
import { render } from "react-dom";
import { Segment, Form, Checkbox, Button } from "semantic-ui-react";
import axios from "axios";
export default class ShowForm extends React.Component {
constructor(props) {
super(props);
this.state = {
event: ""
};
}
handleCheck = event => {
console.log("Hello", event);
};
render() {
return (
<div>
<Segment>
<Form>
<Checkbox label="Cake" onChange={this.handleCheck} />
<br />
<Checkbox label="Lights" onChange={this.handleCheck} />
<br />
<Checkbox label="Flowers" onChange={this.handleCheck} />
<br />
<Checkbox label="Toys" onChange={this.handleCheck} />
<br />
<Button onClick={this.handleSubmit}> Submit </Button>
</Form>
</Segment>
</div>
);
}
}
Here is whole code: "https://codesandbox.io/s/zealous-paper-p9zb5"
Can anyone please help me in this issue?
You need an array to store all the checked status.
handleCheck = id => () => { // Get indentify from each element
const { checkedList } = this.state;
const result = checkedList.includes(id) // Check if checked at this moment
? checkedList.filter(x => x !== id) // If checked, remove
: [...checkedList, id]; // If not, add
this.setState({ checkedList: result }, () => { // setState
console.log(this.state.checkedList); // setState is async, log in callback
});
};
And if you want, you can make the Checkbox component a common one so you don't need to bind the label in three places in each of it.
<Checkbox
label="Cake"
onChange={this.handleCheck("Cake")}
checked={checkedList.includes("Cake")} // If included, checked
/>
Another minimum reproducible demo
Try it in-text:
const list = [...Array(10).keys()].map(x => ({ id: x }));
const App = () => {
const [selected, setSelected] = React.useState([]);
const onChangeHandler = id => () => {
selected.includes(id)
? setSelected(selected.filter(x => x !== id))
: setSelected([...selected, id]);
};
const onRemove = () => {
setSelected([]);
};
return (
<div className="App">
{list.map(item => (
<input
type="checkbox"
key={item.id}
checked={selected.includes(item.id)}
onChange={onChangeHandler(item.id)}
/>
))}
<br />
<button onClick={onRemove}>Remove all</button>
<div>{selected.join(",")}</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Pretty sure, you can ES6 named properties and arrow functions to create a clean flow
Consider a state:
this.state={
isToy: false,
isCake: false,
isFlower: false,
isLight: false
}
Add a property of Name. These names should ideally match the ones in state.
<Form>
<Checkbox name="isCake" label="Cake" checked={this.state.isCake} onChange={this.handleCheck} />
<Checkbox name="isLight" label="Lights" checked={this.state.isLight} onChange={this.handleCheck} />
<Checkbox name="isFlower" label="Flowers" checked={this.state.isFlower} onChange={this.handleCheck} />
<Checkbox name="isToy" label="Toys" checked={this.state.isToy} onChange={this.handleCheck} />
<Button onClick={this.handleSubmit}> Submit </Button>
</Form>
Then write the handleCheck arrow function, using ES6 name properties feature to access state property:
handleCheck = (event) => {
let name = event.target.name
this.setState{[name]: !this.state[name]}
}
References:
React Controlled Forms: https://reactjs.org/docs/forms.html
Semantic UI Checkboxes: https://react.semantic-ui.com/modules/checkbox/#usage-remote-control
This is a simplified scenario.
I have a form with a required input field and a button. The button has an onClick React handler and is of type "button". I want the browser to check the HTML fields and do some initial validation (like it would do if no React were involved and the button were of type "submit"). I imagine I should do something in the handler function, but I am not sure what.
A few things I tried:
Changing the button to type "submit" does perform the check, but also calls the handler, which does not know whether the check succeeded or failed
Adding the handler on the form instead works, but makes the real example harder to maintain because I have a lot of buttons
Thank you
<div id="app"></div>
class MyClass extends React.PureComponent {
render() {
return (
<form action="#">
<input type="text" required/>
<button type="button" onClick={e => this.handle(e)}>Press</button>
</form>
)
}
handle(event) {
// What should I do here?
}
}
ReactDOM.render(<MyClass />, document.querySelector("#app"))
https://jsfiddle.net/89wr3ot4/
It looks like form has a checkValidity() and reportValidity() API. The answer then becomes
class MyClass extends React.PureComponent {
render() {
return (
<form action="#" ref={this.formRef}>
<input type="text" required/>
<button type="button" onClick={e => this.handle(e)}>Press</button>
</form>
)
}
handle(event) {
const form = this.formRef.current;
if (!form.checkValidity()) {
form.reportValidity()
return
}
// Everything else
}
}
ReactDOM.render(<MyClass />, document.querySelector("#app"))
You need to create state for input value
const [inputValue, setInputValue] = useState(''); //for functional component
const inputHandler = (event) => setInputValue(event.target.value);
then
<input type='text' value={inputValue} onChange={inputHandler} />
and check in your 'handler' function what you want.
handle(event) {
if (inputValue.length > 0) //do what you want...
}
Following is working example which is modified from above jsfiddle
class MyClass extends React.Component {
state = { value: '', message: ''}
render() {
return (
<form action="#">
<input type="text" required value={this.state.value} onChange={e => this.setState({value: e.target.value})} />
<button type="button" onClick={e => this.handle(e)}>Press</button>
<p> {this.state.message }</p>
</form>
)
}
handle(event) {
// What should I do here?
const { value } = this.state;
if (value === '') {
this.setState({message: 'Invalid!, Please enter valid input'})
} else {
this.setState({message: 'Yeah!, Got Valid input'})
}
}
}
ReactDOM.render(<MyClass />, document.querySelector("#app"))
I'm new to React and I'm trying to create a modal that will be used to display different content. I have currently set this up to have different 'mode' states and each mode will display different markup, which gets passed into the Modal component as this.props.children
It seems to work to an certain extent but I'm having problems with state handling. The input works fine updating and displaying the current state of the input, but once this content is nested within the Modal component it does some strange things like no longer allowing you to type or show any key input at all, if there is state content, any keypresses are only updating the state with the last character.
I'm guessing this is because the Modal is a stateful component with a new construtor, the reference to 'this' and the 'handleChange' function within the parent app is lost.
Any ideas on where I'm going wrong or how to properly go about this?
Cheers guys :)
The code is here:
import React, { Component } from "react";
import ReactDOM from "react-dom";
class Modal extends Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.show !== this.props.show;
}
componentWillUpdate() {
console.log("[Modal] WillUpdate");
}
render() {
return (
<div
style={{
transform: this.props.show ? "translateY(0)" : "translateY(-100vh)",
opacity: this.props.show ? "1" : "0",
padding: "20px",
border: "1px solid"
}}
>
{this.props.children}
</div>
);
}
}
class App extends Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
this.state = {
newItem: "",
modalOpen: false,
modalMode: ""
};
}
handleChange(e) {
const target = e.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
openModal(mode) {
this.setState({
modalOpen: true,
modalMode: mode
});
}
render() {
console.log(this.state);
let modalContent;
switch (this.state.modalMode) {
case 'addItem':
modalContent = (
<form>
<h1>Add Item</h1>
<input
type="text"
name="newItem"
value={this.state.newItem}
placeholder="Enter an item"
onChange={this.handleChange}
/>
</form>
)
break;
case 'editItem':
modalContent = (
<div>
</div>
)
break;
default:
modalContent = (null)
}
return (
<div>
<button onClick={() => this.openModal('addItem')}>Open Modal</button>
<Modal show={this.state.modalOpen}>
{modalContent}
</Modal>
<h2>Same inputs outside modal</h2>
<input
type="text"
name="newItem"
value={this.state.newItem}
placeholder="Enter an item"
onChange={this.handleChange}
/>
<input
type="text"
name="newItem"
value={this.state.newItem}
placeholder="Enter an item"
onChange={this.handleChange}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And a code sandbox here - https://codesandbox.io/s/qzx14rmy6
Easy and fast fix could be remove the "value" sync and the state will update just fine.
<input type="text" name="newItem" placeholder="Enter an item" onChange={this.handleChange} />
I pointed out the real problem. In "shouldComponentUpdate", if you return "false" for any reason, component in NOT update with state. This method should be used carefully as it can break your app. Remove the method or return true and component will work just fine.
Can someone please tell me what the hell I am doing wrong here? I am pretty new to React, and everything I have read says that this should work. I am trying to call the function "addItem" when the form is submitted, however the console.log throws the error "Expected onSubmit listener to be a function, instead got type boolean" on page load. Thanks!
import React, { Component } from 'react';
import App from "./App"
import List from "./List"
class AddTodo extends Component{
constructor(){
super();
this.state = {
items: []
}
}
addItem(e){
var itemArray = this.state.items;
itemArray.push({
text: this._inputElement.value,
key: Date.now()
})
this.setState({
items: itemArray
})
this._inputElement.value = "";
console.log(itemArray)
e.preventDefault()
}
render(){
return(
<div className="box-wrapper">
<form onSubmit={this.addItem.bind(this)}>
<input href={(a) => this._inputElement = a} placeholder={this.props.placeHolder} type="text"></input>
<button type="submit">Submit</button>
</form>
<List entries={this.state.items} />
</div>
)
}
}
export default AddTodo;
Try changing your render and addItem to something like this:
addItem(e){
e.preventDefault();
{/* add the rest of the function here */}
}
render(){
return(
<div className="box-wrapper">
<form onSubmit={this.addItem.bind(this)}>
<input
href={(a) => this._inputElement = a}
placeholder={this.props.placeHolder}
type="text"
/>
<button type="submit" onClick={this.addItem.bind(this)}>Submit</button>
</form>
<List entries={this.state.items} />
</div>
)
}
I made two important changes:
Added e.preventDefault(); to the addItem method, which will prevent default behavior.
Added an onClick handler to the "submit" button, with the addItem method passed in as the target executable.
First have to remove event.preventDefault .It is restricting the default behaviour of the webpage.Add a onClick event listener to the submit so that the function executes on button click.
`import React, { Component } from 'react';
import App from "./App"
import List from "./List"
class AddTodo extends Component{
constructor(){
super();
this.state = {
items: []
}
}
addItem(e){
var itemArray = this.state.items;
itemArray.push({
text: this._inputElement.value,
key: Date.now()
})
this.setState({
items: itemArray
})
this._inputElement.value = "";
console.log(itemArray)
// e.preventDefault(); remove this prevent default
}
render(){
return(
<div className="box-wrapper">
<form onSubmit={this.addItem.bind(this)}>
<input href={(a) => this._inputElement = a} placeholder={this.props.placeHolder} type="text"></input>
<button type="submit" onClick={this.addItem.bind(this)}>Submit</button>
</form>
<List entries={this.state.items} />
</div>
)
}
}
export default AddTodo;`
I am not getting any error. But you should put the preventDefault at the top of the addItem, this will prevent from page reload. There is better way to handle the input value by assigning ref attribute to input and accessing via this.refs.refName.
addItem(e) {
e.preventDefault(); //To prevent the page reload on submit
var itemArray = this.state.items;
itemArray.push({
text: this.refs.inputElement.value
});
this.refs.inputElement.value = ""; // clearing the value
}
render() {
return(
<div>
<form onSubmit={this.addItem.bind(this)} >
<input ref="inputElement" placeholder={this.props.placeHolder} type="text"/>
<button type="submit">Submit</button>
</form>
{
this.state.itemArray.map((item) => {
return(<span>{item}</span>)
})
}
</div>
);
}
I having the following problem.
I have a react component which does a filter search on a list which is fine, but I want to clear the input field with a button, if i add a value attribute to the input field which I can set in the setState but then it stops the onChange from working as it does the filter search on the list:
I've created the following but this still doesn't work.
<input type="text" ref="form" placeholder="Search indicators" className="uk-width-1-1" onChange={this.handleChange} />
<button className="uk-button uk-width-1-1" onClick="React.findDOMNode(this.refs.form).reset()"><i className="uk-icon-undo"></i></button>
set the value to empty as
handleClick = () => {
ReactDOM.findDOMNode(this.refs.form).value = "";
}
and yes onClick expects a function or a value not a string. Also React.findDOMNode() is deprecated. You should use ReactDOM.findDOMNode();
class Hello extends React.Component {
handleChange = (e) => {
}
handleClick = () => {
ReactDOM.findDOMNode(this.refs.form).value = "";
}
render() {
return (
<div>
<input type="text" ref="form" placeholder="Search indicators" className="uk-width-1-1" onChange={this.handleChange} />
<button className="uk-button uk-width-1-1" onClick={this.handleClick}><i className="uk-icon-undo"></i>click</button>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.js"></script>
<div id="app"></div>
Also Its a better idea to use state and change the state for input value.
as
class Hello extends React.Component {
constructor() {
super()
this.state = {
valueAttr : ''
}
}
handleChange = (e) => {
this.setState({valueAttr: e.target.value});
}
handleClick = () => {
this.setState({valueAttr: ''})
}
render() {
return (
<div>
<input type="text" ref="form" placeholder="Search indicators" className="uk-width-1-1" value = {this.state.valueAttr} onChange={this.handleChange} />
<button className="uk-button uk-width-1-1" onClick={this.handleClick}><i className="uk-icon-undo"></i>click</button>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.js"></script>
<div id="app"></div>
I personally prefer the second approach. But you can do it in whichever way you feel comfortable.
Here onClick="React.findDOMNode(this.refs.form).reset()" you are assigning string to onClick value of button, not a function, if you want to assign a function it should look like onClick={React.findDOMNode(this.refs.form).reset}" or onClick={() => React.findDOMNode(this.refs.form).reset()}
Next, if i recall correctly, findDOMNode is deprecated in React, so you should use ReactDOM for this.
And last, I doubt there is reset method present, so I think you should try mutating input value directly.
Try this
onClick={() => this.refs.form.value = ''}
Just use:
this.refs.form.getInputNode().value = '';
Should do the trick