I was developing a react component to get a value inside a input and automatically show it in a tag, using refs.
All works fine, but the value shown is the previous value.
I really don't now how to fix this. I using the onChange event in the input to change the state of what will be shown, it is clear that the present value is not taken, but rather the previous value
class Conversor extends Component {
constructor(props){
super(props)
this.state = {
value: null
}
this.output = this.output.bind(this)
}
output(){
console.log(this.state)
this.refs.output.innerHTML = this.state.value
}
render() {
return (
<div>
<h2>{this.state.inputValue}</h2>
<input ref="input" type="text" onChange={() => {this.setState({ value: this.refs.input.value }); this.output()}}/>
<label ref="output"></label>
</div>
);
}
}
If i put the value "Hello World" in the input, the value shown is "Hello Worl", when it's have to be the "Hello World"
You can use event to do this and no need of output() function.
class Conversor extends Component {
constructor(props){
super(props)
this.state = {
value: null
}
}
render() {
return (
<div>
<h2>{this.state.inputValue}</h2>
<input ref="input" type="text" onChange={(e) => {this.setState({ value: e.target.value });}}/>
<label ref="output">{this.state.value}</label>
</div>
);
}
}
The best way to achieve your goal is not using the refs. Here is how you do it
class Conversor extends Component {
constructor(props){
super(props)
this.state = {};
}
handleChange = (e) => {
const { id, value } = e.target;
this.setState({
[id]: value
})
}
render() {
const { name, anotherName } = this.state;
return (
<div>
<h2>{name}</h2>
<input id="name" name="name" type="text" onChange={this.handleChange}/>
<h2>{anotherName}</h2>
<input id="anotherName" name="anotherName" type="text" onChange={this.handleChange}/>
</div>
);
}
}
If you still want to use the refs then do the following,
class Conversor extends Component {
constructor(props){
super(props)
this.state = {
value: null
}
}
output = (e) =>{
this.setState({value: e.target.value }, () => {
this.refs.output.innerHTML = this.state.value
})
}
render() {
return (
<div>
<input ref="input" type="text" onChange={this.output}/>
<label ref="output"></label>
</div>
);
}
}
You don't need to bind your input handler function at all. Instead of doing that, just use an arrow function like _handleInputTextChange . Check this out:
import React, { Component } from 'react';
class InputTextHandler extends Component {
constructor(props){
super(props)
this.state = {
inputValue: ''
}
}
_handleInputTextChange = e => {
const inputValue = e.target.value;
this.setState({inputValue})
console.log(inputValue)
}
render() {
return (
<div>
<input
type="text"
onChange={this._handleInputTextChange}/>
</div>
);
}
}
export default InputTextHandler;
Two things: grab the event value in the onChange method, and pass the this.output method as the second argument to setState which fires after the state has been updated which is not a synchronous operation.
render() {
return (
<div>
<h2>{this.state.inputValue}</h2>
<input ref="input" type="text" onChange={event => {this.setState({ value:event.target.value }, this.output)}}/>
<label ref="output"></label>
</div>
);
}
Try it here!
Related
I have a textarea where I want to have an onChange event. When text is entered, I want to compute the length of the string entered and then pass it to another react component to display the character count. However, I'm having trouble passing the data into my react component.
I have 3 react components in total:
SegmentCalculator: this is my full app
InputBox: this is where a user would enter their string
CharacterBox: this is where I'd like to display my character count
Here's what I have so far:
class InputBox extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
}
}
render() {
return (
<label>
Input:
<textarea
type="text"
value={this.state.value}
onChange={() => this.props.onChange(this.state.value)}
/>
</label>
);
}
}
class CharacterBox extends React.Component {
render() {
return (
<div>Character Count:{this.props.charCount}</div>
)
}
}
class SegmentCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
inputChars: null,
};
}
handleChange(value) {
this.setState({
inputChars: value,
inputCharsLength: value.length,
});
}
render() {
let charaterCount = this.state.inputCharsLength;
return (
<div className="segment-calculator">
<div className="input-box">
<InputBox onChange={() => this.handleChange()} />
</div>
<div className="characters">
<CharacterBox charCount={charaterCount}/>
</div>
</div>
);
}
}
You have a semi-controlled input, meaning, it has local state but you don't update it.
Pass the input state from the parent.
InputBox - Pass the value prop through to the textarea element. Pass the onChange event's target value to the onChange callback prop.
class InputBox extends React.Component {
render() {
return (
<label>
Input:
<textarea
type="text"
value={this.props.value}
onChange={(e) => this.props.onChange(e.target.value)}
/>
</label>
);
}
}
SegmentCalculator - Pass this.state.inputChars to the InputBox value prop. The input length is derived state so there is no reason to store it in state.
class SegmentCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
inputChars:'',
};
}
handleChange = (value) => {
this.setState({
inputChars: value,
});
}
render() {
const { inputChars } = this.state;
return (
<div className="segment-calculator">
<div className="input-box">
<InputBox
onChange={this.handleChange}
value={inputChars}
/>
</div>
<div className="characters">
<CharacterBox charCount={inputChars.length}/>
</div>
</div>
);
}
}
class InputBox extends React.Component {
render() {
return (
<label>
Input:
<textarea
type="text"
value={this.props.value}
onChange={(e) => this.props.onChange(e.target.value)}
/>
</label>
);
}
}
class CharacterBox extends React.Component {
render() {
return (
<div>Character Count:{this.props.charCount}</div>
)
}
}
class SegmentCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
inputChars: '',
};
}
handleChange = (value) => {
this.setState({
inputChars: value,
});
}
render() {
const { inputChars } = this.state;
return (
<div className="segment-calculator">
<div className="input-box">
<InputBox
onChange={this.handleChange}
value={inputChars}
/>
</div>
<div className="characters">
<CharacterBox charCount={inputChars.length}/>
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<SegmentCalculator />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
started learning react. I've been sitting on this problem for an hour and I have no idea why this is not working. Looking everywhere, but without any results. Am I dumb or what?
I cannot write in input field or when I can (if I fix it) then my state doesn't change. Maybe someone knows why is that a problem?
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
text: "test"
}
}
textChangedHandler = (event) => {
this.setState = ({
text: event.target.value
});
}
render() {
return (
<div className="App">
<p>{this.state.text}</p>
<input
type="text"
onChange={this.textChangedHandler}
value={this.state.text}>
</input>
</div>
);
}
}
export default App;
see your textChangedHandler
textChangedHandler = (event) => {
this.setState = ({
text: event.target.value
});
}
In this,
you need to set state like this.
this.setState({
text: event.target.value
})
this.setState is a method. You should need to reassign it.
textChangedHandler = (event) => {
this.setState({
text: event.target.value
});
}
render() {
return (
<div className="App">
<p>{this.state.text}</p>
<input
type="text"
onChange={this.textChangedHandler}
value={this.state.text}>
</input>
</div>
);
}
try this.
Try this one:
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
text: "test"
}
// This binding is necessary to make `this` work in the callback
this.textChangedHandler = this.textChangedHandler.bind(this)
}
textChangedHandler = (event) => {
this.setState({
text: event.target.value
});
}
render() {
return (
<div className="App">
<p>{this.state.text}</p>
<input
type="text"
onChange={this.textChangedHandler}
value={this.state.text}>
</input>
</div>
);
}
}
export default App;
More information: https://reactjs.org/docs/handling-events.html
Here is my simple to-do app program where I have made only one component which takes in the input form user and passes that input value to App.js to update items in App.js state.
todo-form.component.js
import React from 'react';
class SignInForm extends React.Component {
constructor(){
super();
this.state ={
temp: null
};
}
handleChange = (e) => {
e.preventDefault();
this.setState({
temp: e.target.value
},
console.log(this.state)
);
// this.props.addInput(e.target.value);
}
handleSubmit= (e) => {
e.preventDefault();
console.log(this.state.temp);
this.props.addInput(this.state.temp);
}
render(){
return(
<div className="container-form">
<form onSubmit={this.handleSubmit}>
<input
name="description"
type="text"
placeholder="add description"
onChange={this.handleChange}
value={this.state.input}
/>
<button type="submit">ADD</button>
</form>
</div>
);
}
}
export default SignInForm;
App.js
import React from 'react';
import './App.css';
import SignInForm from './components/todo-form/todo-form.component'
import ItemList from './components/todo-list/todo-list.component';
class App extends React.Component {
constructor(){
super();
this.state = {
input: []
};
}
addInput = (item) => {
let newInput=[...this.state.input,item];
console.log(newInput);
this.setState = ({
input: newInput
},
console.log(this.state)
);
}
render(){
return (
<div className="App">
<h1>TO-DO LIST</h1>
<SignInForm addInput={this.addInput} />
</div>
);
}
}
export default App;
On taking input the state inside todo-form.component.js is getting updated with the typed input value but on passing state.temp in handleChange function, the state inside App.js is not updating when addInput function is called.
Please help me on this issue and how my state is not getting updated in App.js??
Your setState is the problem. Have a look at my code.
App.js
class App extends React.Component {
state = {
input: [],
};
addInput = (item) => {
let newInput = [...this.state.input, item];
//setState should be this way
this.setState({
input: newInput,
});
};
render() {
return (
<div className="App">
<h1>TO-DO LIST</h1>
{this.state.input.map((el) => (
<li> {el}</li>
))}
<SignInForm addInput={this.addInput} />
</div>
);
}
}
export default App;
Login file.
class SignInForm extends React.Component {
// constructor(props) {
// super(props);
state = {
temp: null,
};
// }
handleChange = (e) => {
e.preventDefault();
this.setState({
temp: e.target.value,
});
// this.props.addInput(e.target.value);
};
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state.temp);
this.props.addInput(this.state.temp);
};
render() {
return (
<div className="container-form">
<form onSubmit={this.handleSubmit}>
<input
name="description"
type="text"
placeholder="add description"
onChange={this.handleChange}
value={this.state.input}
/>
<button type="submit">ADD</button>
</form>
</div>
);
}
}
export default SignInForm;
I have a React component with an input field.
I want to update the value of the input field when a button is clicked, I can confirm that the value changes when I inspect element but it doesn't display in the input field. Below is a sample code to just to give an idea.
class InputField {
constructor(props) {
super(props)
}
state = {
userInput: ''
}
}
onClick = () => {
this.setState({
userInput: 'Test'
})
}
render() {
return ( <input value={this.state.userInput} name="sampleInput" />
<button onClick={this.onClick}> Click me </button>
)
}
Fix syntax
your code is ok, just little order.
I add the whole component
import React, { Component } from 'react';
class InputField extends Component {
constructor(props) {
super(props)
}
state = {
userInput: ''
}
onClick = () => {
this.setState({
userInput: 'Test'
})
}
render() {
return (
<div>
<input value={this.state.userInput} name="sampleInput" />
<button onClick={this.onClick}>Click me</button>
</div>
)
}
}
export default InputField;
I just removed syntax error in your example and it worked for me.
import React from 'react';
export default class InputField extends React.Component {
constructor(props) {
super(props)
this.state = {
userInput: ''
}
}
onClick = () => {
this.setState({
userInput: 'Test'
})
}
render() {
return (
<div>
<input value={this.state.userInput} name="sampleInput"/>
<button
onClick = {this.onClick}
>
Click me
</button>
</div>
)
}
}
One approach would be to implement this as a functional component via hooks. You could for instance use the state hook to store and render the userInput data as shown below:
import React from "react";
/* Declare functional InputField component */
function InputField () {
/* Define local state hook to store the "user input" data */
const [userInput, setUserInput] = React.useState("");
const onClick = (e) => {
/* Prevent button click's default behavior */
e.preventDefault();
/* Call the state's "setter" method to update "userInput" state */
setUserInput('Test')
}
/* Render both input and button in a <> fragment */
return (<>
<input value={this.state.userInput} name="sampleInput"/>
<button onClick={onClick}>Click me</button>
</>)
}
To use this component, simply render it as:
<InputField />
I just fix your syntax errors and it run no any error
class InputField extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: '',
};
}
onClick = () => {
this.setState({
userInput: 'Test',
});
};
render() {
return (
<div>
<input value={this.state.userInput} name="sampleInput" />
<button onClick={this.onClick}>Click me</button>
</div>
);
}
}
I have a simple form on child component A. On submit I'm passing the data from the form to the parent component and storing the data in state.. I want to then move this data to a different child, child component B.(the sibling of A)
I'm having trouble getting the data to be rendered on submit in component B. I'm not sure how to trigger the rendering on submit or how to pass this information via props on submit.
Here is the Parent
class Msg extends React.Component {
constructor(props) {
super(props);
this.storeInput = this.storeInput.bind(this);
this.state = {
name: '',
msg: ''
};
}
storeInput (d) {
this.setState({
name: d.name,
msg: d.msg
})
}
render() {
return(
<div className='msgContainer'>
<Input
passBack={this.storeInput}/>
<Output />
</div>
)
}
}
Here is Component A
class Input extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
name: '',
msg: ''
};
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
this.props.passBack(this.state);
}
render () {
const name = this.state.name;
const msg = this.state.msg;
return (
<div className='form-container'>
<form action="">
<label htmlFor="">name</label>
<input
name='name'
value={name}
onChange={this.handleChange}
type='text'></input>
<label htmlFor="">message</label>
<textarea
name='msg'
value={msg}
onChange={this.handleChange}
rows='5' cols='80'></textarea>
<input
onClick={this.handleSubmit}
type='submit'></input>
</form>
</div>
)
}
}
Here is Component B
class Output extends React.Component {
render () {
return(
<div className='output'>
</div>
)
}
}
Simply pass the state as props to Output like so:
Parent Component:
import React from 'react';
import Input from './Input';
import Output from './Output';
class Msg extends React.Component {
state = { name: '', msg: '' };
storeInput = d => {
this.setState({ name: d.name, msg: d.msg });
};
render() {
// destructure the state
const { name, msg } = this.state;
return (
<div className="msgContainer">
<Input passBack={this.storeInput} />
{/* pass the state as props to Output */}
<Output name={name} msg={msg} />
</div>
);
}
}
export default Msg;
Input.js
import React from 'react';
class Input extends React.Component {
state = { name: '', msg: '' };
handleChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
handleSubmit = e => {
e.preventDefault();
this.props.passBack(this.state);
this.setState({ name: '', msg: '' }); // clear up the input after submit
};
render() {
const { name, msg } = this.state;
return (
<div className="form-container">
<form onSubmit={this.handleSubmit}>
<label htmlFor="">name</label>
<input
name="name"
value={name}
onChange={this.handleChange}
type="text"
/>
<label htmlFor="">message</label>
<textarea
name="msg"
value={msg}
onChange={this.handleChange}
rows="5"
cols="80"
/>
<input type="submit" />
</form>
</div>
);
}
}
export default Input;
Output.js
import React from 'react';
// destructure the props coming in from Msg
// no need for a class-based component
const Output = ({ name, msg }) => (
<div className="output">
<div>Output</div>
<p>{name}</p>
<p>{msg}</p>
</div>
);
export default Output;
Live demo: https://jsfiddle.net/c8th67zn/