Not a fan of mixing jQuery with React but I liked a color picker called Farbstastic so I embedded it as a component in my React app. There is an input field as type hidden where the value gets changed when user interact the with color picker. I want to grab the value in React using onChange event.
This is my react code to grab the change:
class Color extends Component {
render() {
return (
<div>
<label htmlFor="color">Color:</label>
<input type="hidden" id="color" name="color" value="#123456" onChange={event => { console.log(event.target.value);} }/>
<div id="picker"></div>
</div>
)
}
}
The problem is it does not grab the change on value by jQuery.
You use case is called uncontrolled components in react world.
https://reactjs.org/docs/uncontrolled-components.html
So basically you have to use ref to access value of this input in your react class methods.
<input type="hidden" id="color" name="color" value="#123456"
ref={(input) => this.colorCodeInput = input}/>
<div id="picker"></div>
Now in your submit function you can access the value set like
onSubmit() {
let colorCode = this.colorCodeInput.value.
}
Related
while learning the concept controlled components in Reactjs I came across this statement
controlled component: "A component which renders a form, but the source of truth for that form state lives inside of the component state rather than inside of the DOM"
what is a form state? and what does it mean by form state living inside of DOM or component?
could you please elaborate on it?
what is a form state?
The form state just means "the current values entered into the individual input elements of a form". They can be accessed through the individual input elements value property or through the <form> elements form data itself.
what does it mean by form state living inside of DOM or component
It just means that if you do not explicitly store those values in react state and pass them to the <input> during render it will just be kept in the HTML input elements itself. If you inspect an <input> element in the developer console you will see, that it has a value prop. Uncontrolled just means that you do not explicitly pass that value to the <input> and that you do not handle changes to the value yourself. If you need to access that value in your code you would have to get a ref to the <input> and read that value.
function UncontrolledForm() {
const handleSubmit = event => {
event.preventDefault();
// values are stored in the form but they can be accessed through the DOM node
const formData = new FormData(event.target);
console.log(Object.fromEntries(formData.entries()));
};
return (
<form onSubmit={handleSubmit}>
<label>
foo
<input name="foo" />
</label>
<label>
bar
<input name="bar" />
</label>
<button>submit</button>
</form>
);
}
Controlled on the other hand means that you "control" the value yourself by passing it to the <input> during render all the time.
function ControlledForm() {
// values are kept in react state
const [values, setValues] = useState({foo: '', bar: ''});
const handleChange = event =>
setValues(state => ({
...state,
[event.target.name]: event.target.value
}));
const handleSubmit = event => {
event.preventDefault();
console.log(values);
};
return (
<form onSubmit={handleSubmit}>
<label>
foo
{/* we explicitly pass a value and onChange handler and manage the values ourself */}
<input name="foo" value={values.foo} onChange={handleChange} />
</label>
<label>
bar
<input name="bar" value={values.bar} onChange={handleChange} />
</label>
<button>submit</button>
</form>
);
}
I'm building a mortgage calculator app and I want to have it update in real-time in front of the user. To do this I need to update state whenever input is changed so that the component rerenders.
I plan to do this using the onChange event on each input field, my question is should the onChange event call a different function for each input to update that state property, or is there a way to have one function, and change the state property that is being updated.
For example here is my class Component with the form, state, and onInputChange function
class Calculator extends Component {
state = {
price: 250000,
term: 20
};
onInputChange = (event) => {
this.setState({ price: event.target.value })
};
render() {
return (
<div>
<form>
<label htmlFor="propertyPrice">Property Price</label>
<input
type="number"
id="propertyPrice"
name="propertyPrice"
value={this.state.price}
onChange={this.onInputChange}
/>
<label htmlFor="term">Mortgage Term</label>
<input
type="term"
id="term"
name="term"
value="3"
/>
</form>
</div>
);
}
}
As you can see on the Property Price input I'm using the onChange event to call the function onInputChange, which directly updates the price in the state. Do I need a new function for the mortgage term input, or can I use the one function and just change which state property I'm updating, if so, how can I determine which state property is being updated in the function?
I do it this way:-
Create a single onInputChange() like this
onInputChange = (name, value) = {
this.setState({[name]: value})
}
Now create any number of inputs but make sure your onChange handler receives a function like this
onChange = {(e) => onInputChange(nameOfInput, e.target.value)}
Make your form input tags name same as state name and then you can add this for every input's onChange handler.
const handleChange = (e) => {
this.setState({[e.target.name]: e.target.value});
}
How to clear the value inside the input in function Admin after I click the "Add" button? Should i use another class based component instead of a functional component?
I have set the value of one of the input box as : value={props.item} and in the this.setState I update the value of item as item:"".
AddInfo(info){
let s = this.state.products;
let obj ={name:""};
obj.name=info.productName;
s.push(obj);
this.setState({
products:s,
item:"" //Here i set the value of item equal to an empty string.
})
console.log(this.state.products);
}
function Admin(props){
let productName="";
return (
<div>
<input type="text" required placeholder="Product Name" onChange={(e)=>{productName=e.target.value}} value={props.item}></input><br/>
<button type="Submit" onClick{(e)=>props.AddInfo({productName})}>Add</button>
</div>
)
}
You have to save your input within a local state of the input function:
AddInfo(info){
let s = this.state.products;
let obj ={name:""};
obj.name=info.productName;
s.push(obj);
this.setState({
products:s,
})
console.log(this.state.products);
}
function Admin(props){
const [productName, setProductName] = useState('');
return (
<div>
<input type="text" required placeholder="Product Name" onChange={(e)=> setProductName(e.target.value) value={productName}></input><br/>
<button type="Submit" onClick{(e)=> {props.AddInfo({productName}); setProductName('')}}>Add</button>
</div>
)
}
This will work for you, since you are not mutating the productName variable anymore but now you are saving it in a local state of that input function.
Hope this helps!
Admin is like a form, and the main decision you have to make is rather you want it to be controlled (info is stored in stated, and state is reflected in the ui), or uncontrolled (info is taken from the dom once 'Add' is clicked.
Since you want to empty the input once 'Add' is clicked it makes sense to make this component controlled.
The next decision is rather you want it to be a functional component, or a class component. In nowadays it doesn't really matter (functional components can now use state with the state hook).
To store state in you functional component use React's useState hook.
function Admin({addInfo}){
const [productName, setProductName] = useState(")
return (
<div>
<input
type="text"
placeholder="Product Name"
onChange={(e)=>{
setProductName(e.target.value)
}
value={prodcutName}>
</input>
<button
onClick{(e)=>{
props.addInfo({productName})
setProductName("") // Will set the input to an empty string
}
>
Add
</button>
</div>
)
}
I'm learning React and as a learning exercise am trying to do a very basic page where there is a form and you put text in an input box, you click submit and the header changes to what you entered. Here is my code so far:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {header: 'yeaheheh'}
}
changeHeader(e) {
let newHeader = document.getElementById('input').value();
e.preventDefault();
console.log('submitted');
this.setState(newHeader);
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader.bind(this)} className="change-header-form">
<input id="input" type="text" placeholder="Enter Text Here" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default App;
At first, when I clicked submit, nothing happened and I got an error in the console that says
Uncaught TypeError: Cannot read property 'setState' of null
I then realized I needed to bind the changeHeader function to this which I changed so before I had:
<form onSubmit={this.changeHeader}...
changed it to
<form onSubmit={this.changeHeader.bind(this)}...
After doing this, the error cleared but my header is still not updating.I read that there has been strong suggestions against changing state via setState is bad practice because calling setState() again could potentially alter the changed state. setState is also an asynchronous operation which would also explain why my header isn't changing.
With all that said, then what would be the best way to handle this? From what I understand, props wouldn't make sense either since those values are stored directly in your component and aren't parameters that can't be dynamically updated. I'm having a hard time understanding the relationship between these different data types and how they are handled in the DOM.
You are setting state incorrectly.
More over to get the data from input fields you can either use controlled input elements(via states) or uncontrolled input elements via "ref" which I have used in below example.
In controlled input element you store the value of input element in state and changes to that value is done by calling onChange method and then setting the state via this.setState({}).
Calling setState causes re-rendering to happen and dom gets the updated data based on new state.
Btw "refs" gets you the direct access to dom elements, in similar way $() was used in jquery and should be avoided if possible because it will lead to very hard to manage and predict dom changes.
Also there are cases where use of "refs" is recommended
There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
class App extends React.Component {
constructor() {
super();
this.state = {header: 'yeaheheh'};
}
changeHeader = (e) => {
e.preventDefault();
let newHeader = this.textInput.value;
console.log('submitted');
this.setState({header : newHeader});
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader} className="change-header-form">
<input id="input" ref={(input) => { this.textInput = input; }} type="text" placeholder="Enter Text Here" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('test'));
<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="test">
</div>
Replace this.setState(newHeader); with this.setState({header: newHeader});.
Take a look at this article in the react docs: https://facebook.github.io/react/docs/forms.html#controlled-components.
Basically what you want to do is create another handler for the input. This will be called every time there is a change to the input field and a property in your state will be updated. Then, when you submit the form you can take that new property and "merge" it using setState to become the new header.
JS Bin
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
header: 'yeaheheh',
next: ''
}
this.changeHeader = this.changeHeader.bind(this);
this.updateNext = this.updateNext.bind(this);
}
changeHeader(e) {
e.preventDefault();
this.setState({
header: this.state.next
});
}
updateNext(e) {
this.setState({
next: e.target.value
});
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader} className="change-header-form">
<input id="input" type="text" placeholder="Enter Text Here" onChange={this.updateNext} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Maybe this bin will provide a little better context at what I'm trying to describe.
There's the small bug in your code preventing it from working (this.setState(newHeader) -> this.setState({header: newHeader});), but the thing is that your code is not idiomatic for React.
You are supposed to use controlled components instead of grabbing the values from the form's inputs on submit, as you would do with jQuery.
"Controlled component" is a silly name for the pattern where an input's state is mapped to the application state, so an input itself behaves as if it would be kinda "stateless". In your case, you need to have separate component state member for every text input you've got. Input control should look like this:
<input value={ this.state.inputValue }
onChange={ e => this.setState({ inputValue : e.target.value }) }
/>
Now it's bound to your inputValue state member, so you can just take it from the state at any moment you need. On form's submit handler, in your case.
That's it. Your code must be fixed accordingly. Refer to the "controlled components" manual for further details, it's the really important React concept.
You should modified your function like this..
constructor(props) {
super(props);
_that = this;
}
changeHeader = (e) => {
e.preventDefault();
let newHeader = this.textInput.value;
console.log('submitted');
_that.setState({header : newHeader});
}
With the following method:
handleClick(event) {
const inputText = this.refs.inputText
console.log(inputText.value.trim())
}
I am trying to get Material-UI's <TextField/> to return the input text correctly with ref like the <input/> can with <button/> triggering it:
<input
className='form-control'
placeholder='Input Text'
ref='inputText'
type='text'
/>
<button
onClick={(event) => this.handleClick(event)}
>
And I attempted the following with <TextField/>, but it returns as undefined. How can I get it to return inputted text correctly like the <input/> above?
<TextField
hint='Enter text'
className='form-control'
ref='inputText'
type='text'
/>
I would suggest this approach:
Set up your textfield with a value and onChange function that are hooked into redux itself, where the onChange function just updates the value.
So you'd have something like this :
<TextField
value={this.props.textFieldValue}
onChange={this.props.textFieldChange}
Where the textFieldChange is an action that simply updates the textFieldValue. Most forms in redux will work something like this. Keep in mind the names i made up for those props and action are just for example. If you have a big form you might want to consider have part of the state tree dedicated to the form itself where you have :
state: {
form: {
textField: ...your textfield value here,
name: ...,
whateverElse: ...
}
};
I like doing this with redux because I can make that architect form part of the state to look like the json payload of wherever I'm sending it to, so there I can just send the form went I want to send it.
Anyways, back to this example. When you click your handleClick now. All you need to do is this to get the value:
handleClick(event) {
console.log(this.props.textFieldValue.trim());
}
Because the textfield is updated with every change, you always have access to it in your state. This also gives you flexibility over the refs approach, because if you use refs you will have a lot harder of a time getting access to that form in other components. With this approach, all the information is on your state so you can access it anytime, as long as you manage your props.
You should use the onChange={} to get the value:
_onChange = (e) => {
console.log(e.target.value);
}
<TextField
onChange={this._onChange}
/>
Here's a better solution than using onchange event, we get directly the value of the input created by material-ui textField :
create(e) {
e.preventDefault();
let name = this.refs.inputText.input.value;
alert(name);
}
constructor(){
super();
this.create = this.create.bind(this);
}
render() {
return (
<form>
<TextField ref="inputText" hintText="" floatingLabelText="Your name" /><br/>
<RaisedButton label="Create" onClick={this.create} primary={true} />
</form>
)}
hope this helps.