Changing state but then clearing input React - javascript

I'm writing my first React app and really struggling to do something quite basic.
I have an Input component that has an array in state, which, when it has two numbers, it sends them and a unique ID as an object up to a parent Component which stores the object in an array.
This is all fine and I can do it. The problem is clearing the inputs afterwards.
So far as I understand it, I need the value of the inputs to be stored in the Component state (in the array) when I do an on Change. Those values are then used for submitting the form.
However, if the inputs are getting their value from state, they need to have a value on render, which I don't want. I only want them to have a value after I've entered something into the input. I've tried using setState to replace the inputTable with an empty array after submission, but that's still not changing the values.
Here's the code - to reiterate, I want to find a way to just clear the inputs after I've submitted the array. At the moment it keeps saying that I'm changing an uncontrolled component into a controlled one, which I understand, but I don't understand how else I'm meant
Please trust that I've tried to solve this by myself, checking out MDN docs about forms and inputs, but I'm really not getting anywhere. I'd really appreciate the help.
import React, { Component } from 'react';
class Input extends Component {
constructor(props) {
super(props)
this.state = {
inputTable: [],
uniqueId: 1
}
this.handleChange = this.handleChange.bind(this);
this.sendTables = this.sendTables.bind(this);
}
async handleChange(e) {
e.preventDefault();
await this.setState({
inputTable: [
...this.state.inputTable, e.target.value
]
})
console.log(this.state.inputTable)
// how do I handle this onChange correctly?
}
async sendTables(e) {
e.preventDefault();
this.props.submitTable(this.state.inputTable, this.state.uniqueId);
let newArray = [];
await this.setState({
inputTable: newArray,
uniqueId: this.state.uniqueId + 1
})
console.log(this.state.inputTable)
// how can I clear the inputs?
}
render() {
return (
<div className="Input">
<form action="" onSubmit={this.sendTables}>
<input required type="number" name="value0" placeholder="a number..." onChange={this.handleChange} value={this.state.inputTable[0]} />
<span>X</span>
<input required type="number" name="value1" placeholder="multiplied by..." onChange={this.handleChange} value={this.state.inputTable[1]}/>
<input type="submit" value="Add times table" />
</form>
</div>
);
}
}
export default Input;
Parent component:
import React, { Component } from 'react';
import Input from './Input/Input';
class InputTimesTables extends Component {
constructor() {
super();
this.state = {
tables: [],
noInputs: 1
}
this.pushToTables = this.pushToTables.bind(this);
}
addInput() {
// this will be used to add an additional input
// it will increment no. inputs
}
async pushToTables(arr, id) {
let newTimesTable = {
timesTable: arr,
uniqueId: id
}
await this.setState({
tables: [...this.state.tables, newTimesTable]
})
// console.log(`The ITT state looks like:`, this.state.tables);
//this will take the two numbers in the array from the Input
// and push them to the tables array
// it won't run unless there are two numbers in that array
console.log('The main state array now looks like this: ', this.state.tables)
}
// clearTables(id){
// console.log(`splicing array no ${id}`);
// let newArray = this.state.tables;
// newArray.splice(id, 1);
// this.setState({
// tables: newArray
// })
// console.log(this.state.tables);
// // console.log(`The ITT state looks like:`, this.state.tables);
// }
render() {
return (
<div>
<Input submitTable={this.pushToTables}/>
<h3>Currently being tested on:</h3>
<ul>
</ul>
</div>
);
}
}
export default InputTimesTables;
Many thanks.

I think I've got it - the issue is that a number input is not actually a number - it's a string. So I can just set the arrays to ["", ""] and then reset them, and there's no problem. I hope this helps someone else if they run into it.
Thanks if you had a look!

Related

How to properly use React.creatRef()

I've been beating my head against the wall and cross referencing code I've read from others and questions that have been asked. I'm at the point where I know I'm just not looking in the right direction, however I'm not sure where to turn.
The app I'm writing was originally written by me in 2019, and I did not know of React h
Hooks as I learned components from a coding bootcamp. TL;DR: I'm rusty.
The issue:
I'm trying to use AXIOS to use a PUT call into my MongoDB, and the way I learned was by using refs. Refs in the way I learned is now deprecated, and I just want to get this working so I can move on to another project and start using Hooks instead.
When I use my button created to save the change, I get an error in the browser console, however it refreshes too fast for me to catch the exact error. The second I get to look at the wall of text, it looks similar to a bad promise, but I'm not entirely sure. Either way, the data does not get updated.
My code:
import React, { Component } from 'react'
import axios from 'axios'
export default class EditSeed extends Component {
constructor(props){
super(props)
this.state = {
_id: '',
seed: '',
created_at: '',
__v: ''
}
this.changeHandler = this.changeHandler.bind(this)
this.myRef = React.createRef
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
componentDidMount(){
this.fetchSeed()
}
fetchSeed = async () => {
try {
const res = await axios.get(`/api/grapevine/${this.props.match.params.id}`)
this.setState({
_id: res.data._id ,
seed: res.data.seed,
created_at: res.data.created_at,
__v: res.data.__v
})
}
catch (err) {
this.setState({ error: err.message })
console.log(err)
}
}
editSeed = async (newSeed) => {
try {
const res = await axios.request({
method: 'put',
url: `/api/grapevine/${this.state._id}`,
data: newSeed
})
res.this.props.history.push(`/`)
}
catch (err) {
this.setState({ error: err.message })
console.log(err)
}
}
onSubmit = (e) => {
const newSeed = {
seed: this.myRef.current.seed.value,
created_at: this.myRef.current.created_at.value
}
this.editSeed(newSeed)
e.preventDefault()
}
changeHandler = (e) => {
const target = e.target
const value = target.value
const name = target.name
this.setState({
[name]: value
})
}
render(){
return(
<div>
<h1>Edit Seed</h1>
<form onSubmit={this.onSubmit.bind(this)}>
<label>
Edit Message:
<input type="text" name="seed" ref={this.myRef} value={this.state.seed} onChange={this.changeHandler} />
</label>
<input type="submit" value="Save" />
</form>
</div>
)
}
}
My fetch function works as intended, it's mainly just the edit that doesn't want to work. I have tried changing
<input type="text" name="seed" ref={this.myRef} value={this.state.seed} onChange={this.changeHandler} />
to
<input type="text" name="seed" ref={this.myRef.current} value={this.state.seed} onChange={this.changeHandler} />
Obviously I am doing something wrong, and I have referenced and read the React docs on this about 10 times and I'm really just hitting a wall.
Any insight would be massively appreciated.
Firstly, React.createRef is a function, so it needs to be invoked.
this.myRef = React.createRef();
Then you simply attach the ref to the element.
<input
ref={this.myRef}
type="text"
name="seed"
value={this.state.seed}
onChange={this.changeHandler}
/>
Then whenever you need to reference the input's ref you access the current value of it.
this.myRef.current
Update
I don't think the React ref is providing much for you here since you duplicate the seed and created_at into local state. You update the seed state via the onChange handler. Why not just forego the ref and access the state in the submit handler. I believe this will resolve your "cannot access value of undefined" error since the input element target object likely doesn't have seed or created_at attributes to access a value of.
onSubmit = (e) => {
e.preventDefault();
const newSeed = {
seed: this.state.seed,
created_at: this.state.created_at // is this supposed to be a current datetime?
}
this.editSeed(newSeed);
}
changeHandler = (e) => {
const { name, value } = e.target;
this.setState({
[name]: value
});
}
You did nearly got it right, just a few minor changes will lead you to the right way:
First: You need to invoke React.createRef function:
this.myRef = React.createRef -> this.myRef = React.createRef()
Second: You need to call e.preventDefault() from the begining of your handler so that the whole page will not get refeshed imediately. Something like this:
onSubmit = (e) => {
e.preventDefault();
const newSeed = {
seed: this.myRef.current.seed.value,
created_at: this.myRef.current.created_at.value
}
this.editSeed(newSeed)
}
From then, I think you will easily get to the right path.
You can use useRef hook in functional components like this:
import React, {useRef} from 'react';
function MyComponenet () {
myRef = useRef(null) // <---- initialize your ref with null
return (
<div ref=myRef >
Hello World
</div>
)
}
now you create a reference and initiate it with a null value, then passed it to the div element. from now you can manipulate your ref like get its value or assign values, click, and other actions.
with TypeScript
myRef = useRef<HTMLDivElement>(null)
if you are passing myRef to a div element, so your ref type will be HTMLDivElement

React JS search in Array (character by character)

This is my first program in React. I've as below:
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name : ''
}
}
render() {
return (
<>
<div>
<label for="searchEmp">Search Person: </label>
<input type="text" value={this.state.name} id="searchEmp"
placeholder="Enter Person's Name"
onChange={event => this.setState({name: event.target.value})}/><br/>
</div>
{["John","Steve","Alen","Stephen",
"Smith","Alex","Jack","Andy","Jacky"].map(item => {
return <div>{item}</div>})}
</>
);
}
}
export default App;
In Output, I've something like as
I want to filter this list character by character. For e.g. When I enter S the list should filtered with names starting from S as below:
Next, If I enter t after S the list should contain only names as:
and so on. How can I get this? Apart, as a newbie to React, Is my code okay? Thanks in advance.
So, first of all I think the good practice would be ti either keep the list as constant one if it's fixed or some variable at class level,
Scenario you are asking for is preety much like you want to filter out the list each time, so you can filter out the list kept in variable using a function & could return the list from the function to use
let namesList = ['ab', 'fg', 'test'];
input = 'a';
let rgxp = new RegExp(input, "g");
function findFilterNames() {
return namesList.filter(x => x.match(rgxp));
}
test = findFilterNames();
console.log(test);
I'll try my best to answer but apologies in advance if this doesn't work, I've just started to learn React as well.
Whenever you use setState, the component will re-render itself, so keeping that in mind, you could use the following:
Create a function that looks for this.state.name and checks to see if its blank or has an actual value. If it does have a value, it will either use filter or map to run through the name array and return div elements with the values placed inside.
See code below:
class App extends React.Component {
constructor(props) {
super(props)
this.state = { name: '' };
}
renderNames() {
this.names =["John","Steve","Alen","Stephen","Smith","Alex","Jack","Andy","Jacky"];
if(this.state.name !== '') {
return this.names.map((name) => {
if (name.includes(this.state.name)){
return <div>{name}</div>;
}
});
} else {
return this.names.map((name) => {
return <div>{name}</div>;
});
}
}
render() {
return (
<>
<div>
<label for="searchEmp">Search Person: </label>
<input type="text" value={this.state.name} id="searchEmp"
placeholder="Enter Person's Name"
onChange={event => this.setState({name: event.target.value})}/><br/>
</div>
{this.renderNames}
</>
);
}
}
I will keep it simple and easy, you must use a function in Javascript called filter() with includes() function.
I will assume the search term that coming from the input called term, and the array that you need to filter it called names
const names = ["John","Steve","Alen","Stephen","Smith","Alex","Jack","Andy","Jacky"];
let term = "s";
const searchResults = names.filter(name => {
return name.toLowerCase().includes(term.toLowerCase());
});
// the result will be array that contain the following ["Steve","Stephen","Smith"]
when you consloe.log(searchResults) you will have ["Steve","Stephen","Smith"] because term value is "s" so you get the all names that have "s" character.
We change the array items to lowercase using toLowerCase() to avoid the case-senstive if there is character upper case
you can test the code here just put the name of the filtered searchResults at the end of the code
I see this way as the fastest way to search by character.
this is a repo as an example using Reactjs :
https://github.com/Tawfeekamr/react-search-in-array-character-by-character.git

React state with calculated fields

I have a react component, which has properties and state. Some fields of state contain input data (uplifted from input control), but there is also fields in the state that must be Calculated based on current State and Props:
The question: what is the best way to update calculated fields of the state (based on other fields of state and props)?
Ugly way to do it:
componentDidUpdate(){
this.setState({calculatedField:calculate(this.props,this.state)}))
}
In this case I get infinite loop of updates or in the best case (if I use PureComponent) double rendering invocation.
The best solution I found so far (but still ugly):
Is to create a calculated object in state, which contains calculated fields and updated in componentWillUpdate avoiding setState:
componentWillUpdate(nextProps,nextState){
nextState.calculated.field1=f(nextProps,nextState)
}
class ParentComponent extends React.Component {
constructor(props, ctx) {
super(props,ctx)
this.state={A:"2"}
}
render() {
console.log("rendering ParentComponent")
return <div>
<label>A=<input value={this.state.A} onChange={e=>{this.setState({A:e.target.value})}} /></label> (stored in state of Parent component)
<ChildComponent A={this.state.A} />
</div>
}
}
class ChildComponent extends React.PureComponent {
constructor(props,ctx) {
super(props,ctx);
this.state={
B:"3",
Calculated:{}
}
}
render() {
console.log("rendering ChildComponent")
return <div>
<label>B=<input value={this.state.B} onChange={e=>{this.setState({B:e.target.value})}} /></label> (stored in state of Child component state)
<div>
f(A,B)=<b>{this.state.Calculated.result||""}</b>(stored in state of Child component)
<button onClick={e=>{ this.setState({Calculated:{result:new Date().toTimeString()}}) }}>Set manual value</button>
</div>
</div>
}
componentWillUpdate(nextProps, nextState) {
this.state.Calculated.result = getCalculatedResult(nextProps.A, nextState.B)
}
componentWillReceiveProps(nextProps) {
this.state.Calculated.result = getCalculatedResult(nextProps.A, this.state.B)
}
componentWillMount() {
this.state.Calculated.result = getCalculatedResult(this.props.A, this.state.B)
}
}
function getCalculatedResult(a,b) {
const aNum = Number(a)||0
const bNum = Number(b)||0;
const result = (aNum*bNum).toString();
return result;
}
ReactDOM.render(<ParentComponent/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
This is also ugly solution and React does not recommended to mutate state avoiding setState. So what is right solution for that?
NOTE:
In my real application I cannot recalculate f(a,b) every single time during rendering, because it's actually complex object, so I need to cache it somehow and the best way is in the state.
If you are using React 16.8.0 and above, you can use React hooks API. I think it's useMemo() hook you might need. For example:
import React, { useMemo } from 'react'
const MyComponent = ({ ...props }) => {
const calculatedValue = useMemo(
() => {
// Do expensive calculation and return.
},
[a, b]
)
return (
<div>
{ calculatedValue }
</div>
)
}
For more details, refer to the React documentation
I wouldn't advice you to store your calculated value inside your state. My approach would be more like this:
import PropTypes from 'prop-types';
import React from 'react';
class Component extends React.Component {
static defaultProps = { value: 0 };
static propTypes = { value: PropTypes.number };
state = { a: 0, b: 0 };
result = () => this.state.a + this.state.b + this.props.value;
updateA = e => this.setState({ a: +e.target.value });
updateB = e => this.setState({ b: +e.target.value });
render() {
return (
<div>
A: <input onChange={this.updateA} value={this.state.a} />
B: <input onChange={this.updateB} value={this.state.b} />
Result: {this.result()}
</div>
);
}
}
The problem with storing the calculation inside your state is, that the calculation can be mutated by multiple sources. If you use my solution, there is no way, that anything can overwrite the calculation WITHOUT using the correct function to calculate them.
You can save calculated result in this.calculated instead of this.state. It is dependent data. All data which causes update and render is already in state and props.
class Component extends React.Component {
constructor(props) {
super(props)
state = {
b: 0
}
}
updateThis = (event) => {
this.setState({ b: event.target.value });
}
componentWillUpdate(nextProps,nextState){
this.calculated.field1=f(nextProps.a, nextState.b)
}
render() {
return (
<form>
A = <input onChange={this.props.updateParent} value={this.props.a} /> <br>
B = <input onChange={this.updateThis} value={this.state.b} /> <br>
f(A,B) = {this.calculated.field1} <br>
</form>
);
}
}
class ParentComponent extends React.Component {
constructor(props) {
super(props)
state = {
a: 0
}
}
render() {
return (
<Component
updateParent={event=>this.setState({a: event.target.value})}
a={this.state.a}
/>
}
}
}
You're first attempt is the right way to solve this problem. However, you need to add a check to see if state has actually changed:
componentDidUpdate(prevProps, prevState){
if(prevState.field !== this.state.field){
this.setState({calculatedField:calculate(this.props,this.state)}))
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.calculatedField !== nextState.calculatedField
}
You need to check the pieces of state and props that you use in your calculate method and make sure they have changed before updating state again. This will prevent the infinite loop.
It looks like the "state" is the place to store everything (even computed values) you'll need to use on the render function, but usually we have the problem you describe.
Since React 16.3 a new approach for this situation has been given in the way of the static getDerivedStateFromProps (nextProps, prevState) "lifecycle hook".
You should update at least to this version if you haven't, and follow the advice given by the React Team on their blog.
Here is the official documentation for this functionality.
The issue here is that this function is invoked before every render, and being "static" you cannot access the current instance previous props, which is usually needed to decide if the computed value must be generated again or not (I suppose this is your case, as you have stated your computation process is heavy). In this case, the React team suggests to store in the state the values of the related props, so they can be compared with the new ones:
if (nextProps.propToCompute !== prevState.propToComputePrevValue) {
return {
computedValue: Compute(nextProp.propToCompute),
propToComputePrevValue: nextProps.propToCompute
};
}
return null;
Do not include redundant information in your state.
A simplified example is having firstName and lastName in your state. If we want to display the full name in your render method, you would simply do:
render() {
return <span>{`${this.state.firstName} ${this.state.lastName}`}</span>
}
I like this example because it's easy to see that adding a fullName in our state, that just holds ${this.state.firstName} ${this.state.lastName} is unnecessary. We do string concatenation every time our component renders, and we're okay with that because it's a cheap operation.
In your example, your calculation is cheap so you should do it in the render method as well.

use a single handler for multiple inputs onChange events

I have bunch of inputs and I do not want to have multiple handlers like
handleInput1(){},handleInput2(){} and so on.
But I have difficulties producing below array of object
[{
name: 3,
value: 1000
},{
name: 5,
value: 1000
}]
how can I by using listen to only one handler and use setState in react?
http://jsbin.com/godesacici/edit?js,console,output
You can try to do it the follwoing way by distinguishing the different inputs by the name attribute and storing the result in the state
class HelloWorldComponent extends React.Component {
constructor(){
super();
this.state = {
result: []
}
}
handleInput(e) {
console.log(e.target.value);
var result = [...this.state.result];
var idx = result.map(function(val){ return val.name}).indexOf(e.target.name);
if(idx > -1) {
result[idx].value=e.target.value;
} else {
result.push({name: e.target.name, value:e.target.value});
}
this.setState({result})
}
handleClick() {
console.log(this.state.result);
}
render() {
return (
<div>
<div><input type="number" name="4" onChange={this.handleInput.bind(this)}/></div>
<div><input type="number" name="3" onChange={this.handleInput.bind(this)}/></div>
<div><input type="number" name="5" onChange={this.handleInput.bind(this)}/></div>
<button onClick={this.handleClick.bind(this)}>submit</button>
</div>
);
}
}
React.render(
<HelloWorldComponent name="Joe Schmoe"/>,
document.getElementById('react_example')
);
JSBIN
So you can be explicit and bind the key string onto a single handler function like so:
_handleInput(key, val) {
let { ..state } = this.state;
state[key] = val;
this.setState(state);
}
render() {
return <div>
<input
onChange={this.handleInput.bind(null, key1)}
value={this.state.key1} />
<input
onChange={this.handleInput.bind(null, key2)}
value={this.state.key2} />
</div>
}
Since it's an an array you want to modify you can use array indices. Suppose the initial state is this.
this.state = {
array: [{
name: 3,
value: 1000
},{
name: 5,
value: 1000
}]
}
Then the inputs can be like this (for the one with name 3 which has index 0)
<input value={this.state.array[0].value} onChange={this.handleChange.bind(this,0)}/>
So the value it will display is for the 1st element in the array (with index 0) and the handleChange binds to the event as well as pass the index 0.
handleChange(index,event){
this.setState((prevState) => ({array:[
...prevState.array.slice(0, index),
Object.assign({},prevState.array[index],{value:event.target.value}),
...prevState.array.slice(index + 1)
]}))
}
Ok so this might seem a little complicate but let me try to explain here. So the handleChange method takes two parameters - the event corresponding to the input text and the index which is the array index of the element in the state array (0 in our example). So in this.setState we have taken the prevState and used a bit of splicing. ...prevState.array.slice(0,index) corresponds to all elements of the array before the one we are modifying. ...prevState.slice(index+1) corresponds to all those after. So we take these two sets and join them with the modified element in between. The Object.assign() corresponds to the modified element. What it is doing is taking the prevState.array[index] which is the element we are modifying and setting it's value to event.target.value corresponding to the text.
If you change your state model to have a key per form element and use some nice-to-haves like arrow functions to capture variable scope in a cleaner syntax, you can simplify things:
class HelloWorldComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
"3": {},
"4": {},
"5": {}
}
}
handleInput(name, value) {
this.setState({
[name]: {
name: name,
value: value
}
});
}
handleClick() {
console.log(this.state);
}
render() {
return (
<div>
<div><input type="number" value={this.state["3"].value} onChange={(e) => this.handleInput(3, e.target.value)}/></div>
<div><input type="number" value={this.state["4"].value} onChange={(e) => this.handleInput(4, e.target.value)}/></div>
<div><input type="number" value={this.state["5"].value} onChange={(e) => this.handleInput(5, e.target.value)}/></div>
<button onClick={(e) => this.handleClick()}>submit</button>
</div>
);
}
}
React.render(
<HelloWorldComponent name="Joe Schmoe"/>,
document.getElementById('react_example')
);
Having your state be an array of values not keyed by anything will force you to search through the state and replace it (as some of the other answers have shown).
It's usually a better idea to simplify things to improve readability and comprehension
Recall that React state is additive, so calling setState with just a partial state change will merge it with the existing state. You will only get this benefit if you're keying your data in the state.
you can add name property to input, and get target.name like this:
_handleInput(event) {
let name = event.target.name;
let value = event.target.value;
this.setState({[name] : value});
}
<input
onChange={this._handleInput}
value={this.state.key1}
name="key1"
/>

How to get the value of an input field using ReactJS?

I have the following React component:
export default class MyComponent extends React.Component {
onSubmit(e) {
e.preventDefault();
var title = this.title;
console.log(title);
}
render(){
return (
...
<form className="form-horizontal">
...
<input type="text" className="form-control" ref={(c) => this.title = c} name="title" />
...
</form>
...
<button type="button" onClick={this.onSubmit} className="btn">Save</button>
...
);
}
};
The console is giving me undefined - any ideas what's wrong with this code?
There are three answers here, depending on the version of React you're (forced to) work(ing) with, and whether you want to use hooks.
First things first:
It's important to understand how React works, so you can do things properly (protip: it's super worth running through the React tutorial on the React website. It's well written, and covers all the basics in a way that actually explains how to do things). "Properly" here means that you're not writing a web page, you're writing the user interface for an application that happens to be rendered in a browser; all the actual user interface work happens in React, not in "what you're used to from writing a web page" (this is why React apps really are "apps", not "web pages").
React applications are rendered based off of two things:
the component's properties as declared by whichever parent creates an instance of that component, which the parent can modify throughout its lifecycle, and
the component's own internal state, which it can modify itself throughout its own lifecycle.
What you're expressly not doing when you use React is generating HTML elements and then using those: when you tell React to use an <input>, for instance, you are not creating an HTML input element, you are instead telling React to create a React input object that happens to render as an HTML input element when you compile your React app for the web, with event handling that is controlled by React.
When using React, what you're doing is generating application UI elements that present the user with (often manipulable) data, with user interaction changing the state of your application in a way that you define - actions performed by the user may update a component's props or state, which React uses as a signal to generate a new UI representation for changed components, which may cause an update of part of your application interface to reflect the new state.
In this programming model, the app's internal state is the final authority, rather than "the UI your users look at and interact with": if a user tries to type something in an input field, and you did not write anything to handle that, nothing will happen: the UI is a reflection of the application state, not the other way around. Effectively, the browser DOM is almost an afterthought in this programming model: it just happens to be a super convenient UI framework that the entire planet is virtually guaranteed to have access to (but it's not the only one React knows how to work with)
A specific example
So with that covered, let's look how a user interacting with an input element works in React. First, we need to get to having a UI element for the user to interact with:
You wrote a component to manage (i.e. both store and present) some string data for your users, with an onChange function for handling user data.
Your component's rendering code is used by React to generate a virtual DOM that contains an input component (not a DOM <input> element), and binds your onChange handler to that component so that it can be called with React event data (so note that this is not a DOM change event listener, and does not get the same event data that regular DOM event listeners do).
The React library then translates that virtual DOM into a UI users can interact with, and that it will update as the application state changes. Since it's running in the browser, it builds an HTML input element.
Then, your user tries to actually interact with that input element:
Your user clicks on the input element and starts typing.
Nothing happens to the input element yet. Instead, the input events get intercepted by React and killed off immediately.
React turns the browser event into a React event, and calls the onChange function for the virtual DOM component with the React event data.
That function may do something, based on what how you wrote it, and in this case you almost certainly wrote it to update the state of your component with what the user (tried to) type.
If a state update gets scheduled, React will run that state update in the near future, which will trigger a render pass after the update.
During the render pass, it checks to see if the state is actually different, and if so, it generates a temporary second virtual DOM, which it compares to (a part of) your application's virtual DOM, determines which set of add/update/remove operations it needs to perform on you application's virtual DOM so that it looks the same as the new temporary one, then applies those operations and throws away the temporary virtual DOM again.
It then updates the UI so that it reflects what the virtual DOM now looks like.
And after all of that, we finally have an updated DOM on the page the user is actually looking at, and they see what they typed in the input element.
So this is completely different from the regular browser model: instead of the user updating the UI data by typing into a text box first and our code reading "the current value of that text box" to figure out what the state is second, React already knows what the state is, and uses events to update the state first, which leads to a UI update second.
And it is important to remember that all of this happens effectively instantly, so to your user it looks like they typed text into an input element in the same way they would for any random web page, but under the hood things couldn't be more different while still leading to the same result.
So, with that covered, let's look at how to get values from elements in React:
Component classes and ES6 (React 16+ and 15.5 transitional)
As of React 16 (and soft-starting with 15.5) the createClass call is no longer supported, and class syntax needs to be used. This changes two things: the obvious class syntax, but also the thiscontext binding that createClass can do "for free", so to ensure things still work make sure you're using "fat arrow" notation for this context preserving anonymous functions in onWhatever handlers, such as the onChange we use in the code here:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.reset();
}
reset() {
// Always set the initial state in its own function, so that
// you can trivially reset your components at any point.
this.state = {
inputValue: ''
};
}
render() {
return (
// ...
<input value={this.state.inputValue} onChange={evt => this.updateInputValue(evt)}/>
// ...
);
},
updateInputValue(evt) {
const val = evt.target.value;
// ...
this.setState({
inputValue: val
});
}
});
You may also have seen people use bind in their constructor for all their event handling functions, like this:
constructor(props) {
super(props);
this.handler = this.handler.bind(this);
...
}
render() {
return (
...
<element onclick={this.handler}/>
...
);
}
Don't do that.
Almost any time you're using bind, the proverbial "you're doing it wrong" applies. Your class already defines the object prototype, and so already defines the instance context. Don't put bind of top of that; use normal event forwarding instead of duplicating all your function calls in the constructor, because that duplication increases your bug surface, and makes it much harder to trace errors because the problem might be in your constructor instead of where you call your code.
"But then it's constantly making and throwing away functions on rerenders!" and that may be true but you're not going to notice. Nor are your users. If event handler garbage collection is your performance bottleneck, so much has already gone wrong that you need to stop and rethink your design: the reason React works so incredibly well is because it does not update the entire UI, it only updates the parts that change, and in a well designed UI, the time that most of your UI spends not changing drastically outnumbers the time small parts of your UI spend updating.
Function components with hooks (React 16.8+)
As of React 16.8 the function component (i.e. literally just a function that takes some props as argument can be used as if it's an instance of a component class, without ever writing a class) can also be given state, through the use of hooks.
If you don't need full class code, and a single instance function will do, then you can now use the useState hook to get yourself a single state variable, and its update function, which works roughly the same as the above examples, except without the "universal" setState function call and using one dedicated state setter for each value you're working with:
import { useId, useState } from 'react';
function myFunctionalComponentFunction(props) {
const id = useId();
const [input, setInput] = useState(props?.value ?? '');
return (
<div>
<label htmlFor={id}>Please specify:</label>
<input id={id} value={input} onInput={e => setInput(e.target.value)}/>
</div>
);
}
Previously the unofficial distinction between classes and function components was "function components don't have state", so we can't hide behind that one anymore: the difference between function components and classes components can be found spread over several pages in the very well-written react documentation (no shortcut one liner explanation to conveniently misinterpret for you!) which you should read so that you know what you're doing and can thus know whether you picked the best (whatever that means for you) solution to program yourself out of a problem you're having.
React 15 and below, using legacy ES5 and createClass
To do things properly, your component has a state value, which is shown via an input field, and we can update it by making that UI element send change events back into the component:
var Component = React.createClass({
getInitialState: function() {
return {
inputValue: ''
};
},
render: function() {
return (
//...
<input value={this.state.inputValue} onChange={this.updateInputValue}/>
//...
);
},
updateInputValue: function(evt) {
this.setState({
inputValue: evt.target.value
});
}
});
So we tell React to use the updateInputValue function to handle the user interaction, use setState to schedule the state update, and the fact that render taps into this.state.inputValue means that when it rerenders after updating the state, the user will see the update text based on what they typed.
addendum based on comments
Given that UI inputs represent state values (consider what happens if a user closes their tab midway, and the tab is restored. Should all those values they filled in be restored? If so, that's state). That might make you feel like a large form needs tens or even a hundred input forms, but React is about modeling your UI in a maintainable way: you do not have 100 independent input fields, you have groups of related inputs, so you capture each group in a component and then build up your "master" form as a collection of groups.
MyForm:
render:
<PersonalData/>
<AppPreferences/>
<ThirdParty/>
...
This is also much easier to maintain than a giant single form component. Split up groups into Components with state maintenance, where each component is only responsible for tracking a few input fields at a time.
You may also feel like it's "a hassle" to write out all that code, but that's a false saving: developers-who-are-not-you, including future you, actually benefit greatly from seeing all those inputs hooked up explicitly, because it makes code paths much easier to trace. However, you can always optimize. For instance, you can write a state linker
MyComponent = React.createClass({
getInitialState() {
return {
firstName: this.props.firstName || "",
lastName: this.props.lastName || ""
...: ...
...
}
},
componentWillMount() {
Object.keys(this.state).forEach(n => {
let fn = n + 'Changed';
this[fn] = evt => {
let update = {};
update[n] = evt.target.value;
this.setState(update);
});
});
},
render: function() {
return Object.keys(this.state).map(n => {
<input
key={n}
type="text"
value={this.state[n]}
onChange={this[n + 'Changed']}/>
});
}
});
Managed to get the input field value by doing something like this:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
username : ''
}
this.updateInput = this.updateInput.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateInput(event){
this.setState({username : event.target.value})
}
handleSubmit(){
console.log('Your input value is: ' + this.state.username)
//Send state to the server code
}
render(){
return (
<div>
<input type="text" onChange={this.updateInput}></input>
<input type="submit" onClick={this.handleSubmit} ></input>
</div>
);
}
}
//output
//Your input value is: x
You should use constructor under the class MyComponent extends React.Component
constructor(props){
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
Then you will get the result of title
In react 16, I use
<Input id="number"
type="time"
onChange={(evt) => { console.log(evt.target.value); }} />
Give the <input> a unique id
<input id='title' ...>
and then use the standard Web API to reference it in the DOM
const title = document.getElementById('title').value
No need to continually update the React state with every keypress. Simply get the value when it's required.
In Function Component
useState
Returns a stateful value, and a function to update it.
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
src ---> https://reactjs.org/docs/hooks-reference.html#usestate
useRef
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
src ---> https://reactjs.org/docs/hooks-reference.html#useref
import { useRef, useState } from "react";
export default function App() {
const [val, setVal] = useState('');
const inputRef = useRef();
const submitHandler = (e) => {
e.preventDefault();
setVal(inputRef.current.value);
}
return (
<div className="App">
<form onSubmit={submitHandler}>
<input ref={inputRef} />
<button type="submit">Submit</button>
</form>
<p>Submit Value: <b>{val}</b></p>
</div>
);
}
In Function Component :-
export default function App(){
const [state, setState] = useState({
value:'',
show:''
});
const handleChange = (e) => {
setState({value: e.target.value})
}
const submit = () => {
setState({show: state.value})
}
return(
<>
<form onSubmit={()=>submit()}>
<input type="text" value={state.value} onChange={(e)=>handleChange(e)} />
<input type="submit" />
</form>
<h2>{state.show}</h2>
</>
)}
export default class App extends React.Component{
state={
value:'',
show:''
}
handleChange=(e)=>{
this.setState({value:e.target.value})
}
submit=()=>{
this.setState({show:this.state.value})
}
render(){
return(
<>
<form onSubmit={this.submit}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" />
</form>
<h2>{this.state.show}</h2>
</>
)
}
}
I succeeded in doing this by binding this to the function
updateInputValue(evt) with
this.updateInputValue = this.updateInputValue.bind(this);
However input value={this.state.inputValue} ...
turned out to be no good idea.
Here's the full code in babel ES6 :
class InputField extends React.Component{
constructor(props){
super(props);
//this.state={inputfield: "no value"};
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick(){
console.log("trying to add picture url");
console.log("value of input field : "+this.state.inputfield);
}
updateInputValue(evt){
//console.log("input field updated with "+evt.target.value);
this.state={inputfield: evt.target.value};
}
render(){
var r;
r=<div><input type="text" id="addpixinputfield"
onChange={this.updateInputValue} />
<input type="button" value="add" id="addpix" onClick={this.handleClick}/>
</div>;
return r;
}
}
your error is because of you use class and when use class we need to bind the functions with This in order to work well. anyway there are a lot of tutorial why we should "this" and what is "this" do in javascript.
if you correct your submit button it should be work:
<button type="button" onClick={this.onSubmit.bind(this)} className="btn">Save</button>
and also if you want to show value of that input in console you should use var title = this.title.value;
This simplest way is to use arrow function
Your code with arrow functions
export default class MyComponent extends React.Component {
onSubmit = (e) => {
e.preventDefault();
var title = this.title;
console.log(title);
}
render(){
return (
...
<form className="form-horizontal">
...
<input type="text" className="form-control" ref={(c) => this.title = c} name="title" />
...
</form>
...
<button type="button" onClick={this.onSubmit} className="btn">Save</button>
...
);
}
};
React Version: 17.0.1
a) Using Functional Components
b) Manage state using hook: useState().
Write and Run code as above:
import React, {useState} from 'react';
const InputElement = () => {
const [inputText, setInputText] = useState('');
return (
<div>
<input
onChange={(e) => {
setInputText(e.target.value);
}
}
placeholder='Enter Text'
/>
{inputText}
</div>
);
}
The solving scheme algorithm is similar to a two-way data binding:
input <=> DATA_MODEL <=> Label_Text
// On the state
constructor() {
this.state = {
email: ''
}
}
// Input view ( always check if property is available in state {this.state.email ? this.state.email : ''}
<Input
value={this.state.email ? this.state.email : ''}
onChange={event => this.setState({ email: event.target.value)}
type="text"
name="emailAddress"
placeholder="johdoe#somewhere.com" />
You can get an input value without adding 'onChange' function.
Just add to the input element a 'ref attr:
And then use this.refs to get the input value when you need it.
Change your ref into: ref='title' and delete name='title'
Then delete var title = this.title and write:
console.log(this.refs.title.value)
Also you should add .bind(this) to this.onSubmit
(It worked in my case which was quite similar, but instead of onClick I had onSubmit={...} and it was put in form ( <form onSubmit={...} ></form>))
if you use class component then only 3 steps- first you need to declare state for your input filed for example this.state = {name:''}. Secondly, you need to write a function for setting the state when it changes in bellow example it is setName() and finally you have to write the input jsx for example < input value={this.name} onChange = {this.setName}/>
import React, { Component } from 'react'
export class InputComponents extends Component {
constructor(props) {
super(props)
this.state = {
name:'',
agree:false
}
this.setName = this.setName.bind(this);
this.setAgree=this.setAgree.bind(this);
}
setName(e){
e.preventDefault();
console.log(e.target.value);
this.setState({
name:e.target.value
})
}
setAgree(){
this.setState({
agree: !this.state.agree
}, function (){
console.log(this.state.agree);
})
}
render() {
return (
<div>
<input type="checkbox" checked={this.state.agree} onChange={this.setAgree}></input>
< input value={this.state.name} onChange = {this.setName}/>
</div>
)
}
}
export default InputComponents
export default class MyComponent extends React.Component {
onSubmit(e) {
e.preventDefault();
var title = this.title.value; //added .value
console.log(title);
}
render(){
return (
...
<form className="form-horizontal">
...
<input type="text" className="form-control" ref={input => this.title = input} name="title" />
...
</form>
...
<button type="button" onClick={this.onSubmit} className="btn">Save</button>
...
);
}
};
using uncontrolled fields:
export default class MyComponent extends React.Component {
onSubmit(e) {
e.preventDefault();
console.log(e.target.neededField.value);
}
render(){
return (
...
<form onSubmit={this.onSubmit} className="form-horizontal">
...
<input type="text" name="neededField" className="form-control" ref={(c) => this.title = c}/>
...
</form>
...
<button type="button" className="btn">Save</button>
...
);
}
};

Categories

Resources