React setting input's value - javascript

I've seen on many React tutorials regarding managing input values. The following pattern:
On Parent passing props to Input Component, handleInputText sets the state for anyValue:
<InputComponent textValue={this.state.anyValue} onInputtingText={this.handleInputText}/>
On Input Component, onEvent can be ==> onChange, onBlur...:
<input type='text' ref='inputRef' value={this.props.textValue} onEvent={this.handleInput}/>
InputComponent's handleInput:
handleInput(){
this.setState(this.refs.inputRef.value)
}
Now my findings, I try to log it when the parents function when setting the state and it logs the initial. This are some tentative conclusions:
Whenever an event its trigger the value of the input is not the current value of the InputComponent. It is the value set on the parent to that value.
Both the input's value and this.props.textValue match on the second triggering of the event.
My question is, how do you handle this the react way? Or do you have to check this inside the handleInput function?
Thanks in advance.

You can set state in this.handleInputText and call it inside InputComponent,
var App = React.createClass({
getInitialState() {
return { anyValue: '' };
},
handleInputtingText(value) {
this.setState({ anyValue: value });
},
render() {
return <div>
<p>{ this.state.anyValue }</p>
<InputComponent
textValue={ this.state.anyValue }
onInputtingText={ this.handleInputtingText }
/>
</div>
}
});
var InputComponent = React.createClass({
handleInput(e) {
this.props.onInputtingText(e.target.value);
},
render: function() {
return <input
type="text"
value={this.props.textValue}
onChange={ this.handleInput }
/>;
}
});
Example

Related

sync state with props to achive two way binding for form input in reactjs

i have a very long form (75 input to be exact), since i'm using redux to manage my application state, whenever i want to edit this form i want to setState of form to the prop to allow editing.
Example Code:
class VisitCard extends Component {
constructor(props) {
super(props); //props.visit = {name:'randome name', data:'etc..'}
this.state = Object.assign({},props.visit);
this.bindInput = this.bindInput.bind(this);
}
//BindInput will return props for Inputs, to achive two way binding
bindInput(config){
const {name,...props} = config;
return {
value : this.state[name],
onChange: event => this.setState({[name]:event.target.value}),
...props
}
}
render(){
return <div>
<input {...this.bindInput({name:'name', type:'text'})} />
<input {...this.bindInput({name:'data', type:'text'})} />
</div>
}
}
above code works perfect, problem is when this component mounts, it give me error "Cannot update during an existing state transition"
also sometimes if the value is not predefined in the props, the value for input will be undefined, so after props load from server and updates component i get another error "trying to change input from uncontrolled to controled" that is because this.state[name] was undefined then i got a value
so what am'i doing wrong ? how can i link the state of component with props value and make sure that if props changed, state does change too, while at sametime, if state changes this does not affect props.
I hope modify your code to match the below logic will resolve your issues. Look for comments inside the code for explanations
class VisitCard extends Component {
constructor(props) {
super(props);
//set your state to have a key that holds your prop value.
this.state = { visit: props.visit };
this.bindInput = this.bindInput.bind(this);
}
componentWillReceiveProps(nextProps) {
//if your props is received after the component is mounted, then this function will update the state accordingly.
if(this.props.visit !== nextProps.visit) {
this.setState({visit: nextProps.visit});
}
}
bindInput(config){
const {name,...props} = config;
// return defaultValue which you get from the props.
// you can add `value: this.state.visit[name]` to the below object only if you want your input to be controlled, else it can be ignored.
return {
defaultValue : this.props.visit[name],
onChange: event => this.setState(
{visit: { ...this.state.visit,
[name]:event.target.value}
}),
...props
}
}
render(){
// render empty if your props has not yet arrived.
if(!this.props.visit) {
return (<div />);
}
// render after you have values in props
return (<div>
<input {...this.bindInput({name:'name', type:'text'})} />
<input {...this.bindInput({name:'data', type:'text'})} />
</div>);
}
}

Notify react components about value change

Suppose that I have a component class which is responsible to change any number entered into textbox to text:
class NumbersToText extends Component {
onChange(event) {
const { target } = event;
const { value } = target;
if (hasNumbers(value)) {
target.value = numbersToText(value);
// HERE I NEED TO NOTIFY ABOUT CHANGES
}
}
render() {
return (
<span onChange={this.onChange}>
{this.props.children}
</span>
);
}
}
Now the usage would look something like this:
<NumbersToText>
<input onChange={this.saveValue}
</NumbersToText>
Let's say that all works, and the value gets changed to text.
Now the problem is that after I change numbers to text and assign that value to input onChange handlers are not executed again, thus saveValue is not called with updated value.
How should this problem be approached in order to trigger onChange handlers with new value?
I don't know exactly what you mean by numbers to text so I'll just assume you want to modify the value before calling the onChange function in the input, and also reflect that value in the input.
First of all, what you're doing will never work on React, React reflects internal virtual objects into the DOM, meaning you shloud not modify the DOM directly and instead you should modify this internal representantion (via setState, props) to reflect this change into the DOM.
There's also two types of inputs on React, controlled and uncontrolled. I will assume you want to use this on uncontrolled inputs.
The only possible solution I can see, is to transform the input using the React.cloneElement function adding a aditional step before calling the input's onChange callback.
Here's a possible implementation that will make the input uppercase.
class UpperCase extends React.Component {
constructor(props) {
super(props);
}
onChange(e, input, next) {
let value = input.value || '';
value = value.toUpperCase();
input.value = value;
next(value);
}
render() {
let childs = React.Children.map(this.props.children, child => {
let input = null; //Will take advantage of javascript's closures
let onChangeChild = child.props.onChange.bind(child);
return React.cloneElement(child, {
ref: ref => input = ref,
onChange: e => {
this.onChange(e, input, onChangeChild)
}
});
});
return (
<span>
{childs}
</span>
);
}
}
And you can use it like this:
<UpperCase>
<input onChange={(val) => console.log(val)}></input>
<textarea onChange={(val) => console.log(val)}></textarea>
</UpperCase>
Thanks to #tiagohngl I came up with a similar, but maybe a little less cluttered (without cloning elements) way:
class NumbersToText extends Component {
onChange(event) {
const { target } = event;
const { value } = target;
if (hasNumbers(value)) {
target.value = numbersToText(value);
this.childrenOnChange(event);
}
}
childrenOnChange(event) {
const { children } = this.props;
React.Children.forEach(children, child => {
if (child.props.onChange) {
child.props.onChange(event);
}
});
}
render() {
return (
<span onChange={this.onChange}>
{this.props.children}
</span>
);
}
}
export default class NumbersToText extends React.Component {
constructor(props) {
super(props)
this.onChange = this.onChange.bind(this);
}
componentWillMount() {
this.setState({ anyData: [] });
}
onChange(event) {
this.setState({anyData: event.target.value},
()=>{console.log("AnyData: "+this.state.anyData)});
// callback to console.log after setState is done
}
render() {
return (
<input type="text"
value={this.state.anyData}
onChange={this.onChange} />
);
}
}
As you mention that,
onChange is not called after changed value.
There are multiple possibilities.
onChange is not binded.
There are no state change in render method, so it will not re-render
make use of console.log() to trace the problem
I slightly ammend the code for illustration.
Hope it helps.
How react handle State Change (answer I posted before)

Discrepancies with setting state values in React

I'm starting out React JS and following this little exercise, https://facebook.github.io/react/docs/more-about-refs.html.
It's fairly simple, but I'm facing a huge discrepancy when setting the state value. When I set the state, I view it in the console by doing this: console.log("s: "+this.state.userInput);. And I also display it in the view with {this.state.userInput}. Fairly simple. But not really. The event and the state value always seem to be a letter apart in the console, but it is perfectly displayed in the view. How is that even possible?!
I wanted to query my server whenever the state changes, but the state is always a letter behind. It's so weird. Could someone please explain to me what this is? And how I can avoid it?
Here's the code.
var SearchContainer = React.createClass({
getInitialState: function() {
return {userInput: ''};
},
handleChange: function(e) {
console.log("e: "+e.target.value);
this.setState({userInput: e.target.value});
console.log("s: "+this.state.userInput);
},
clearAndFocusInput: function() {
this.setState({userInput: ''}); // Clear the input
// We wish to focus the <input /> now!
},
render: function() {
return (
<div>
<div onClick={this.clearAndFocusInput}>
{this.state.userInput}
</div>
<input
value={this.state.userInput}
onChange={this.handleChange}
/>
</div>
);
}
});
And this is the weird output,
Console:
View: (HTML Page)
It's because the state hasn't been updated yet. Even though you've explicitly set it using this.setState, it won't be set until the method has finished executing.
If you need the new value you could always use it from e.target.value.
By default, React components re-render when their state is changed.
Therefore, in order to get an accurate reading of the state at a given point, place your console statement inside of the render function, like so:
var SearchContainer = React.createClass({
getInitialState: function () {
return {
userInput: ''
};
},
handleChange: function(event) {
var value = event.target.value;
console.log('Value is ' + value);
this.setState({
userInput: value
});
},
clearAndFocusInput: function() {
this.setState({
userInput: ''
});
},
render: function() {
var userInput = this.state.userInput;
console.log('State is ' + userInput);
return (
<div>
<div onClick={this.clearAndFocusInput}>
{userInput}
</div>
<input
value={userInput}
onChange={this.handleChange}
/>
</div>
);
}
});

Update parent's state through callback prop from children (React.js)

I have a Page.jsx using a Form.jsx component:
<Form isValid={this.enableButton} isInvalid={this.disableButton}>
<Input validation={{ presence: true }} />
</Form>
The point is: Form needs to check each Input validness to proceed. To achieve this, I am doing this in Form.jsx:
// ...
allInputsAreValid: function () {
return _.all(this.state.inputsValidation, function (inputsValidation) {
return inputsValidation.error === false;
});
}
// ...
Then, in render method of Form.jsx:
if (this.allInputsAreValid()) {
this.props.isValid();
} else {
this.props.isInvalid();
}
Finally, the methods enable/disableButton (on Form.jsx):
// ...
enableButton: function () {
this.setState({
canSubmit: true
});
},
disableButton: function () {
this.setState({
canSubmit: false
});
}
//...
Changing the state in these methods, the console throws an error:
Uncaught Error: Invariant Violation: setState(...): Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
Why? How to fix?
I am assuming "page.jsx" contains methods "enableButton" and "disableButton".
while updating the state do you have any other state properties apart from "canSubmit"? if you have other state properties along with "canSubmit" you are forcing them to become undefined by doing
this.setState({
canSubmit: true
);
So i am assuming there is a state property called "xyz" existed along with "canSubmit" state property. so update the state like below
this.setState({
canSubmit: true,
xyz:this.state.xyz
);
or better you try to use react update addon to update state. more find here https://facebook.github.io/react/docs/update.html
Update
Try the below code in your page.jsx file
shouldComponentUpdate(object nextProps, object nextState){
if(this.state.canSubmit==nextState.canSubmit){
return false
}
}
You need to move this logic out of the render method:
if (this.allInputsAreValid()) {
this.props.isValid();
} else {
this.props.isInvalid();
}
Update:
Let's modify your <Input /> components to accept an onChange prop. I'm not sure if you are using ES6 classes or React.createClass, but I will go the ES6 classes route here.
class Input extends React.Component {
render() {
return <input type="text" onChange={this.props.onChange} />;
}
}
Then modify your Form.jsx to give the inputs an onChange property.
<Form isValid={this.enableButton} isInvalid={this.disableButton}>
<Input
validation={{ presence: true }}
onChange={() => {
if (this.allInputsAreValid()) {
this.props.isValid();
} else {
this.props.isInvalid();
}
}}
/>
</Form>

Input type text's value not getting updated while using React JS

I am using React JS to render an input type="text". I know that if you use the value property React renders a readonly textbox. So, I wrote a small component of my own(see below).
React.createClass({
getInitialState: function() {
var self = this;
return {value: self.renderDefault(self.props.value, '')};
},
handleOnChange: function(event) {
this.setState({value: event.target.value});
if (this.props.onChange){
this.props.onChange(event);
}
},
renderDefault : function(value, defaultValue){
return typeof value !== 'undefined' ? value : defaultValue;
},
render: function() {
var value = this.state.value;
return (<input type="text"
size={this.renderDefault(this.props.size, 1)}
value={value}
onChange={this.handleOnChange}
placeholder={this.renderDefault(this.props.placeholder, '')}
/>);
}
});
Every time I try to render this component with a different value I don't see the component getting updated with the updated value.
Everytime I try to render this component with a different value I don't see the component getting updated with the updated value.
You mean you are running
<MyComponent value={someValue} />
with different values?
If that's the case, the component does not use the new value because you are not telling it to.
The component keeps its state between rerenders and the value shown in the text field comes from the state. If you don't update the state based on the new props, nothing will change. You have to implement componentWillReceiveProps:
componentWillReceiveProps: function(nextProps) {
this.setState({value: nextProps.value});
}
From the docs:
Invoked when a component is receiving new props. This method is not called for the initial render.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
More about lifecycle methods.

Categories

Resources