What is the difference betwen value and defaultValue in react.js [duplicate] - reactjs

I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form.
The behavior I'm looking for is this:
When I open my page, the Text input field should be filled in with the text of my AwayMessage in my database. That is "Sample Text"
Ideally I want to have a placeholder in the Text input field if the AwayMessage in my database has no text.
However, right now, I'm finding that the Text input field is blank every time I refresh the page. (Though what I type into the input does save properly and persist.) I think this is because the input text field's html loads when the AwayMessage is an empty object, but doesn't refresh when the awayMessage loads. Also, I'm unable to specify a default value for the field.
I removed some of the code for clarity (i.e. onToggleChange)
window.Pages ||= {}
Pages.AwayMessages = React.createClass
getInitialState: ->
App.API.fetchAwayMessage (data) =>
#setState awayMessage:data.away_message
{awayMessage: {}}
onTextChange: (event) ->
console.log "VALUE", event.target.value
onSubmit: (e) ->
window.a = #
e.preventDefault()
awayMessage = {}
awayMessage["master_toggle"]=#refs["master_toggle"].getDOMNode().checked
console.log "value of text", #refs["text"].getDOMNode().value
awayMessage["text"]=#refs["text"].getDOMNode().value
#awayMessage(awayMessage)
awayMessage: (awayMessage)->
console.log "I'm saving", awayMessage
App.API.saveAwayMessage awayMessage, (data) =>
if data.status == 'ok'
App.modal.closeModal()
notificationActions.notify("Away Message saved.")
#setState awayMessage:awayMessage
render: ->
console.log "AWAY_MESSAGE", this.state.awayMessage
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
`<div className="away-messages">
<div className="header">
<h4>Away Messages</h4>
</div>
<div className="content">
<div className="input-group">
<label for="master_toggle">On?</label>
<input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
</div>
<div className="input-group">
<label for="text">Text</label>
<input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
</div>
</div>
<div className="footer">
<button className="button2" onClick={this.close}>Close</button>
<button className="button1" onClick={this.onSubmit}>Save</button>
</div>
</div>
my console.log for AwayMessage shows the following:
AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}

Another way of fixing this is by changing the key of the input.
<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />
Update:
Since this get upvotes, I will have to say that you should properly have a disabled or readonly prop while the content is loading, so you don't decrease the ux experience.
And yea, it is most likely a hack, but it gets the job done.. ;-)

defaultValue is only for the initial load
If you want to initialize the input then you should use defaultValue, but if you want to use state to change the value then you need to use value. Personally I like to just use defaultValue if I'm just initializing it and then just use refs to get the value when I submit. There's more info on refs and inputs on the react docs, https://facebook.github.io/react/docs/forms.html and https://facebook.github.io/react/docs/working-with-the-browser.html.
Here's how I would rewrite your input:
awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={#state.awayMessageText} />
Also you don't want to pass placeholder text like you did because that will actually set the value to 'placeholder text'. You do still need to pass a blank value into the input because undefined and nil turns value into defaultValue essentially. https://facebook.github.io/react/tips/controlled-input-null-value.html.
getInitialState can't make api calls
You need to make api calls after getInitialState is run. For your case I would do it in componentDidMount. Follow this example, https://facebook.github.io/react/tips/initial-ajax.html.
I'd also recommend reading up on the component lifecycle with react. https://facebook.github.io/react/docs/component-specs.html.
Rewrite with modifications and loading state
Personally I don't like to do the whole if else then logic in the render and prefer to use 'loading' in my state and render a font awesome spinner before the form loads, http://fortawesome.github.io/Font-Awesome/examples/. Here's a rewrite to show you what I mean. If I messed up the ticks for cjsx, it's because I normally just use coffeescript like this, .
window.Pages ||= {}
Pages.AwayMessages = React.createClass
getInitialState: ->
{ loading: true, awayMessage: {} }
componentDidMount: ->
App.API.fetchAwayMessage (data) =>
#setState awayMessage:data.away_message, loading: false
onToggleCheckbox: (event)->
#state.awayMessage.master_toggle = event.target.checked
#setState(awayMessage: #state.awayMessage)
onTextChange: (event) ->
#state.awayMessage.text = event.target.value
#setState(awayMessage: #state.awayMessage)
onSubmit: (e) ->
# Not sure what this is for. I'd be careful using globals like this
window.a = #
#submitAwayMessage(#state.awayMessage)
submitAwayMessage: (awayMessage)->
console.log "I'm saving", awayMessage
App.API.saveAwayMessage awayMessage, (data) =>
if data.status == 'ok'
App.modal.closeModal()
notificationActions.notify("Away Message saved.")
#setState awayMessage:awayMessage
render: ->
if this.state.loading
`<i className="fa fa-spinner fa-spin"></i>`
else
`<div className="away-messages">
<div className="header">
<h4>Away Messages</h4>
</div>
<div className="content">
<div className="input-group">
<label for="master_toggle">On?</label>
<input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
</div>
<div className="input-group">
<label for="text">Text</label>
<input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
</div>
</div>
<div className="footer">
<button className="button2" onClick={this.close}>Close</button>
<button className="button1" onClick={this.onSubmit}>Save</button>
</div>
</div>
That should about cover it. Now that is one way to go about forms which uses state and value. You can also just use defaultValue instead of value and then use refs to get the values when you submit. If you go that route I would recommend you have an outer shell component (usually referred to as high order components) to fetch the data and then pass it to the form as props.
Overall I'd recommend reading the react docs all the way through and do some tutorials. There's lots of blogs out there and http://www.egghead.io had some good tutorials. I have some stuff on my site as well, http://www.openmindedinnovations.com.

it's extremely simple, make defaultValue and key the same:
<input defaultValue={myVal} key={myVal}/>
This is one of the recommended approaches at https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key

To force the defaultValue to re-render all you need to do is change the key value of the input itself. here is how you do it.
<input
type="text"
key={myDynamicKey}
defaultValue={myDynamicDefaultValue}
placeholder="It works"/>

Maybe not the best solution, but I'd make a component like below so I can reuse it everywhere in my code. I wish it was already in react by default.
<MagicInput type="text" binding={[this, 'awayMessage.text']} />
The component may look like:
window.MagicInput = React.createClass
onChange: (e) ->
state = #props.binding[0].state
changeByArray state, #path(), e.target.value
#props.binding[0].setState state
path: ->
#props.binding[1].split('.')
getValue: ->
value = #props.binding[0].state
path = #path()
i = 0
while i < path.length
value = value[path[i]]
i++
value
render: ->
type = if #props.type then #props.type else 'input'
parent_state = #props.binding[0]
`<input
type={type}
onChange={this.onChange}
value={this.getValue()}
/>`
Where change by array is a function accessing hash by a path expressed by an array
changeByArray = (hash, array, newValue, idx) ->
idx = if _.isUndefined(idx) then 0 else idx
if idx == array.length - 1
hash[array[idx]] = newValue
else
changeByArray hash[array[idx]], array, newValue, ++idx

Related issue
Setting defaulValue on control din't not update the state.
Doing reverse works perfectly:
Set state to default value, and the control UI gets updated correctly as if defaulValue was given.
Code:
let defaultRole = "Owner";
const [role, setRole] = useState(defaultRole);
useEffect(() => {
setMsg(role);
});
const handleChange = (event) => {
setRole(event.target.value );
};
// ----
<TextField
label="Enter Role"
onChange={handleChange}
autoFocus
value={role}
/>

Define a state for your default value
Surround your input with a div and a key prop
Set the key value to the same value as the defaultValue of the input.
Call your setDefaultValue defined at the step 1 somewhere to re-render your component
Example:
const [defaultValue, setDefaultValue] = useState(initialValue);
useEffect(() => {
setDefaultValue(initialValue);
}, false)
return (
<div key={defaultValue}>
<input defaultValue={defaultValue} />
</div>
)

Give value to parameter "placeHolder".
For example :-
<input
type="text"
placeHolder="Search product name."
style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
value={this.state.productSearchText}
onChange={this.handleChangeProductSearchText}
/>

Use value instead of defaultValue and change the value of the input with the onChange method.

Related

React button disabled property not updating

I have trouble using the disabled property on a button with react. The property works if I hard code a value in the input but the button is not re-rendered automatically when the state is updated through an input change.
export default function App() {
const [user, setUser] = useState({ email: "" });
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<form>
<div>
<div>
<label htmlFor="email">
Email
</label>
</div>
<input
type="text"
name="email"
id="email"
placeholder=""
onChange={(e) =>
setUser((previous) => {
previous.email = e.target.value;
return previous;
})
}
/>
</div>
<button type="submit" disabled={user.email === ""}>
Button
</button>
</form>
</div>
);
}
The problem can be experienced live here:
https://codesandbox.io/s/suspicious-kapitsa-z2zj3m?file=/src/App.js
In the mentioned code previous points to the same reference as the new state you are setting. For complex state variables like objects React finds diff of object using shallow comparison and since both variables point to the object there is no rerender.
Creating a copy of the object which points to a different address in memory, and setting the state using that should fix it.
setUser((previous) => {
const newValue = { ...previous };
newValue.email = e.target.value;
return newValue;
})
Link
That is why it is never a good idea to mutate state in React. Always create a new copy and make changes to that. This is the base of pure functions and functional programming.
Just use:
(e) => setUser((previous) => ({...previous, email: e.target.value}))
You trying to mutate the previous state manually is what prevents React from detecting a change (because the reference stays the same)
Also, to use control forms properly, you need to add this prop to your input:
value={user.email}

React Binding syntax

I am making a simple doodle/drawing app for my own tutorial. As a simple first step, I wanted to provide a way for them select the pen color by entering something like Red, Green, or #880000 (hex code).
This is how I implemented it, is there a simpler/neater way?
NOTE: The state variable is defined in the constructor and includes penColor and a few other properties.
<div className='penControls'>
<div>Pen Color
<div className='colorPicker' >
<input type="text" id="penColor" name='penColor' defaultValue={this.state.penColor}
onChange={
(ev) => {
this.state.penColor = document.getElementById('penColor').value;
this.setState(this.state);
}
}
className="penColorPicker mx-4" />
</div>
</div>
</div>
If you're working with React you should avoid the use document API, so there's no need of document.getElementById, use event.target.value that is sent in the event object when calling onChange
Also, never assign a value to your state directly like this
this.state.foo = 'bar';
Use setStatefunction for this:
this.setState({ foo: 'bar' })
That'll dispatch all the component life cycle related to re-rendering in a safe way
Finally, try with the following code...
<div className='penControls'>
<div>Pen Color
<div className='colorPicker' >
<input type="text" id="penColor" name='penColor' defaultValue={this.state.penColor}
onChange={
(ev) => {
const newPenColor = event.target.value;
this.setState({ penColor: newPenColor });
}
}
className="penColorPicker mx-4" />
</div>
</div>
</div>
ReactJS tries to solve the exact problem you are facing of reading from DOM and updating the state. But we need to update the state in React way.
<div className='penControls'>
<div>Pen Color
<div className='colorPicker' >
<input type="text" id="penColor" name='penColor' defaultValue={this.state.penColor}
onChange={
(ev) => {
let newPenColor = this.state.penColor
this.setState({ penColor: newPenColor });
}
}
className="penColorPicker mx-4" />
</div>
</div>
React State Update

ReactJS Radio Buttons

I am having trouble rendering information depending on the selected radio button. What should happen is when the yes button is clicked, 'Welcome user' should appear. When no is selected, 'Please login' should appear.
I am also having trouble with the states. When I click a button initially, the console button shows my state as blank. Then when I click no, the state updates as yes and vise versa. Not sure what is going on here.
render() {
return (
<form onSubmit={this.onFormSubmit}>
<p>Did you go to the lecture?</p>
<label>
<input
type="radio"
value="No"
checked={this.state.goToLectue === 'No'}
onChange={this.handleChange}
/>
No
</label>
<label>
<input
type="radio"
value= "Yes"
checked={this.state.goToLectue === 'Yes'}
onChange={this.handleChange}
/>
Yes
</label>
<div>
<Response goToLecture = {this.state.goToLectue}/>
</div>
);
}
onFormSubmit = e => {
e.preventDefault();
console.log('Chosen: ' + this.state.goToLectue);
};
handleChange = e => {
this.setState({
goToLectue: e.target.value
});
console.log('value of goToLectue onchange: ' + this.state.goToLectue);
}
function outside component
function Response(props) {
if (props.goToLectue)
return <h1>Welcome User</h1>;
else
return <h1>Please Login</h1>;
}
I can see two problems in your code:
1) Response component expects a boolean in its props.goToLectue property, but you're passing it a string ('Yes' or 'No'). Either change state.goToLectue of the main component to also be boolean, or you can do something like <Response goToLecture = {this.state.goToLectue === 'Yes'}/>.
2) In your handleChange method you're logging this.state.goToLectue - this value is stale, you won't see the effect of the setState call immediately but only on the next render() call. You should be logging e.target.value instead.
Hope that helps.
I can see a number of issues.
I have corrected them for you in a Codepen:
https://codepen.io/anon/pen/YRWWEQ
In Response the if statement is being passed the props.goToLecture which can only be 'Yes' or 'No', both these string will resolve as truthy. I added === 'Yes' to correct this. Tip for future is to stick to a boolean value wherever possible as it makes conditional statements a lot neater.
There seemed to be a mismatch of lecture and lectue in the code. I'm guessing the latter was a typo.
<form> tag was missing it's </form>
At the initial render the goToLecture is not set in the state and so it was throwing errors, this might be why you were seeing the blank screen. I added the default state as undefined to allow you the option of showing neither 'response' until a radio button is clicked if you choose.
Hope this helps!

How to clear uncontrolled field in react

I used to use ref for forms but now I always state for forms, I'm facing an issue where I have to clear a field after user submitted something.
handleSumbit = (e) => {
e.preventDefault()
const todoText = this.state.todoText
if(todoText.length > 0){
this.refs.todoTextElem = "" // wont work
this.props.onAddTodo(todoText)
} else {
this.refs.todoTextElem.focus() //worked
}
}
render() {
return(
<div>
<form onSubmit={this.handleSumbit}>
<input ref="todoTextElem" type="text" onChange={e => this.setState({todoText: e.target.value})} name="todoText" placeholder="What do you need to do?" />
<button className="button expanded">Add Todo</button>
</form>
</div>
)
}
Clearing the ref simply don't work because it's a controlled input. I don't want to do something stupid like
passing a flag from parent component telling the form is submitted then use setState to clear the input. Or make onAddTodo to have a callback so that I can do
this.props.onAddTodo(todoText).then(()=>this.state({todoText:""}))
The way you are using the input element is uncontrolled, because you are not using the value property, means not controlling it's value. Simply storing the value in state variable.
You don't need to store the input field value in state variable if you are using ref, ref will have the reference of DOM element, so you need to use this.refName.value to access the value of that element.
Steps:
1- Write the input element like this:
<input
ref= {el => this.todoTextElem = el}
type="text"
placeholder="What do you need to do?" />
To get it's value: this.todoTextElem.value
2- To clear the uncontrolled input field, clear it's value using ref:
this.todoTextElem.value = '';
Write it like this:
handleSumbit = (e) => {
e.preventDefault()
const todoText = this.todoTextElem.value;
if(todoText.length > 0){
this.todoTextElem.value = ''; //here
this.props.onAddTodo(todoText)
} else {
this.todoTextElem.focus()
}
}
Another change is about the string refs, As per DOC:
If you worked with React before, you might be familiar with an older
API where the ref attribute is a string, like "textInput", and the DOM
node is accessed as this.refs.textInput. We advise against it because
string refs have some issues, are considered legacy, and are likely to
be removed in one of the future releases. If you're currently using
this.refs.textInput to access refs, we recommend the callback pattern
instead.
Try and use functional refs instead. Note that the ref is to a DOM element, meaning you still need to address its properties (.value) to modify them as opposed to trying to overwriting the element directly.
The following should work:
handleSumbit = (e) => {
e.preventDefault()
const todoText = this.state.todoText
if(todoText.length > 0){
this.todoTextElem.value = ""
this.props.onAddTodo(todoText)
} else {
this.todoTextElem.focus()
}
}
render() {
return(
<div>
<form onSubmit={this.handleSumbit}>
<input ref={input => this.todoTextElem = input} type="text" onChange={e => this.setState({todoText: e.target.value})} name="todoText" placeholder="What do you need to do?" />
<button className="button expanded">Add Todo</button>
</form>
</div>
)
}

Best Practice For Reading Form Data in React

I'm trying to do something in React that would be very simple in any other framework: I want to collect a bunch of values from a form.
Previously I did this sort of thing with a Backbone View, and it was very simple:
readFormValues: function() {
this.data.foo = this.$('[name="foo"]').val();
this.data.bar = this.$('[name="bar"]:checked').val();
});
But in React I can't seem to find an easy way to do that. It seems my only options are to ...
NOTE: Apologies for the formatting: code blocks and lists don't play well together :-(
Completely bypass React and use the jQuery + e.target to access the form:
handleSubmit: function(e) {
var $form = $(e.target).parents('form:first');
this.data.foo = $form.find('[name="foo"]);
},
render: function() {
return <form onSubmit="handleSubmit"><input name="foo"/></form>;
}
That works, and is simple, but it feels like I'm bypassing React and using JQuery when I should just be using React.
Provide callbacks to every form control:
handleFooClick: function(e) {
this.data.foo = event.target.value;
},
render: function() {
return <form><input name="foo" onChange="handleFooChange"/></form>;
}
This appears to be the React/Flux way, but it feels like a crazy amount of unnecessary work. In my Backbone example I needed just one line per form control, but with this approach every last control I build has to have its own onChange handler (and I have to hook that handler up to every element as I render it).
EDIT: One more disadvantage of this approach is that inside the callbacks this.props and this.state won't point to my form control's props/state (it will point to the input's props/state). This means that not only do I have to write a handler per input AND add that callback to every input as I render, but I also have to pass in my data object to every input!
Use refs:
handleSubmit: function(e) {
this.state.data.foo = this.refs.foo.value;
},
render: function() {
return <form><input ref="foo"/></form>;
}
This seems like a more sane solution, as I only need to add a "ref" attribute to every form control, and then I can read the data as easily as I could in Backbone. However, all the React documentation suggests that using refs that way is wrong (all of the examples using refs involve sending signals to the controls, eg. "focus on this input", not on reading data out of the controls).
I feel like there must be a "React-ive" way to access my form data that isn't needlessly complex, but I'm not seeing it because I don't understand React well enough. If any React expert could explain what I'm missing I would greatly appreciate it.
First, jQuery is an unnecessary dependency and it's not the cleanest option so let's rule it out.
Next, refs have issues with flexibility. See this answer for details. Let's rule refs out for all but the simplest cases.
That leaves option #2 - what Facebook calls controlled components. Controlled components are great because they cover all use cases (like validation on keyup). Although it's not much code, if you'd rather not add a simple change handler for each form element, you might use one change handler for all elements with the use of bind. Something like this:
handleChange: function(fieldName, e) {
console.log("field name", fieldName);
console.log("field value", e.target.value);
// Set state or use external state.
},
render: function() {
var someValue = this.state.someValue; // Or a prop is using external state
return (
<div>
<input
name="someName"
value={someValue}
onChange={this.handleChange.bind(this, "someName")} />
</div>
)
}
Or for an even cleaner way, see this answer.
You can use ReactLink to create a two-way binding between your react component and your state.
Described here: https://facebook.github.io/react/docs/two-way-binding-helpers.html
This is how I handle all fields in a form.
var My_Field=React.createClass({
_onChange: function(evt) {
var e=this.props.parent_form;
if (e) {
e.setState({email: evt.target.value});
}
},
render: function() {
return (
<input type="text" name={this.props.name} placeholder={this.props.label} onChange={this._onChange}/>
);
}
});
var My_Form=React.createClass({
getInitialState: function() {
return {
email: "",
};
},
_onSubmit: function(evt) {
evt.preventDefault();
alert(this.state.email);
},
render: function() {
return (
<form onSubmit={this._onSubmit}>
<My_Field name="email" label="Email" parent_form={this}/>
<input type="submit" value="Submit"/>
</form>
);
}
});
For a smaller project I have been using a single onChange call back for all inputs - something like this...
HandleClick(event) {
let values = this.state.values;
values[event.target.name] = event.target.value;
this.setState({values});
}
This requires that you name your inputs the same as you name their state property but I quite like that. Then you give the value stored in state to the value attribute of your input and you're all set - all your form state stored in one place with a single handler function.
Of course there are more scalable ways - I was just reading about a framework for react called formsy that looked interesting. Here's a tutorial:
http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html
Hope that helps
Dan
You can read form data using only one function.
class SignUp extends Component {
constructor(){
super();
this.handleLogin = this.handleLogin.bind(this);
}
handleLogin(e){
e.preventDefault();
const formData = {};
for(const field in this.refs){
formData[field] = this.refs[field].value;
}
console.log('-->', formData);
}
render(){
return (
<form onSubmit={this.handleLogin} className="form-horizontal">
<div className="form-group text-left">
<label className="control-label col-sm-2">Name:</label>
<div className="col-sm-10">
<input ref="name" type="text" className="form-control" name="name" placeholder="Enter name" />
</div>
</div>
<div className="form-group text-left">
<label className="control-label col-sm-2">Email:</label>
<div className="col-sm-10">
<input ref="email" type="email" className="form-control" name="email" placeholder="Enter email" />
</div>
</div>
<div className="form-group text-left">
<label className="control-label col-sm-2">Password:</label>
<div className="col-sm-10">
<input ref="password" type="password" className="form-control" name="password" placeholder="Enter password" />
</div>
</div>
<div className="form-group text-left">
<div className="col-sm-offset-2 col-sm-10">
<button type="submit" className="btn btn-primary btn-block">Signup</button>
</div>
</div>
</form>
)
} }export default SignUp;
The quickest and easiest way, using React's useState():
const Form = () => {
const [fullName, setFullName] = React.useState("");
const [is18, setOver18] = React.useState(false);
return (
<form>
<input
type="text"
name="fullName"
value={fullName}
onChange={event => setFullName(event.target.value)}
/>
<input
type="checkbox"
name="over18"
checked={is18}
onChange={() => setOver18(!is18)}
/>
</form>
);
};

Resources