What are the differences between defaultValue and value in select? - reactjs

I have two react components, parent and child. I'm passing a prop to a child component, and I want to use that prop to set the defaultValue on a select input. However, if that property changes, I'd like for the select default value to change as well.
When I set default value in select, I can choose one of the options that is a part of that selector. If I use value instead, the 'default' changes as the property updates, but I can't select any of the options.
class Selector extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<select defaultValue={this.props.value}>
<option>1</option>
<option>2</option>
</select>
)
}
}
I'd like for the value to change, and I realize that it is not re rendering even though the prop has changed. I'm looking for a work around.

I'm quoting:
The difference between the defaultValue and value property, is that
defaultValue contains the default value, while value contains the
current value after some changes have been made. If there are no
changes, defaultValue and value is the same.
The defaultValue property is useful when you want to find out whether
the contents of a text field have been changed.
What that actually means is that if you put defaultValue, this value will be initialized to the input and that's it, you can change value and the text will change.
But if you put value, you would need to change that value given to the input in the first place in order for input text to change.
Look at this example, all using the same state, but behaving differently.
// Example class component
class Thingy extends React.Component {
constructor(props) {
super(props);
this.state = { value: 'test' }
}
onChange(e) {
this.setState({ value: e.target.value });
}
render() {
return (
<div>
<div><b>default value</b> (you can edit without changing this.state.value)</div>
<input defaultValue={this.state.value}></input>
<div><b>value</b> (you can't edit because it does not change this.state.value)</div>
<input value={this.state.value}></input>
<div><b>value</b> (you can edit because it has onChange method attached that changes this.state.value) <br /> <b>NOTE:</b> this will also change second input since it has attached the same state with <b>value</b> property, but won't change first input becase same state was attached as <b>defaultValue</b></div>
<input value={this.state.value} onChange={e => this.onChange(e)}></input>
</div>
);
}
}
// Render it
ReactDOM.render(
<Thingy />,
document.body
);
div > div {
font-size: 16px;
}
input + div {
margin-top: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

When you don't have onChange handler you need to put your value as defaultValue, but in value when you have onChange handler.
You can do this,
class Selector extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: props.value
}
}
change = (event) =>{
this.setState({selected: event.target.value});
}
componentDidUpdate(prevProps, prevState) {
if (prevState.selected !== this.props.value) {
this.setState({selected: this.props.value})
}
}
render() {
return (
<select value={this.state.selected} onChange={this.change}>
<option>1</option>
<option>2</option>
</select>
)
}
}

defaultValue is selected value while very first time loading
and
value is selected value every time to change option value

Related

office-ui-fabric-react / TextField input properties alternative to onChanged

I'm currently using the TextField from office UI fabric and using the onChanged property to assign my prop in react the value being entered similar to their GitHub example.
However, the event handler is called for each element being entered. How can I make a call to the event handler(this._onChange) only when the user finishes entering the entire text (eg on focus dismiss, etc)?
I'm guessing that would be more efficient than logging an event with each letter being entered.
New to react. Appreciate your help!
This is more of an in-general way React uses the input onChange event. With React, you want to keep the value of your input in state somewhere, whether that is component state or a global store like Redux. Your state and UI should always be in sync. Because of this, the onChange event fires for every character that is entered/removed so that you can update that state to reflect the new value. Inputs written this way are called Controlled Components and you can read more about them and see some examples at https://reactjs.org/docs/forms.html.
That being said, you can detect when the user leaves the input with the onBlur event, though I would not recommend using that to update the state with the value as you'll see that a Controlled Component will act read-only when you don't update the state in the onChange event. You will have to use an Uncontrolled Component, typically setting the initial value with defaultValue instead of value and making things more difficult for yourself.
// CORRECT IMPLEMENTATION
class ControlledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div>
<h1>Controlled Form</h1>
<input type="text" value={this.state.name} onChange={this.handleNameChange} />
<p>{this.state.name}</p>
</div>
);
}
}
// INPUT DOES NOT WORK
class BuggyUncontrolledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
}
render() {
return (
<div>
<h1>Buggy Uncontrolled Form</h1>
<input type="text" value={this.state.name} />
<p>{this.state.name}</p>
</div>
);
}
}
// NOT RECOMMENDED
class UncontrolledForm extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: 'example'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<div>
<h1>Uncontrolled Form</h1>
<input type="text" defaultValue={this.state.name} onBlur={this.handleNameChange} />
<p>{this.state.name}</p>
</div>
);
}
}
ReactDOM.render(
<div>
<ControlledForm />
<BuggyUncontrolledForm />
<UncontrolledForm />
</div>
, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You may consider using React's onBlur prop which will be invoked when the input loses focus. Here is an example Codepen which window.alert's the <TextField> component's current value when it loses focus: https://codepen.io/kevintcoughlin/pen/zmdaJa?editors=1010.
Here is the code:
const {
Fabric,
TextField
} = window.Fabric;
class Content extends React.Component {
public render() {
return (
<Fabric>
<TextField onBlur={this.onBlur} />
</Fabric>
);
}
private onBlur(ev: React.FocusEvent<HTMLInputElement>) {
window.alert(ev.target.value);
}
}
ReactDOM.render(
<Content />,
document.getElementById('content')
);
I hope you find that helpful.
References
https://reactjs.org/docs/events.html#focus-events
You can keep your state and UI in sync but use things like your own deferred validation error-check functions to check if the value is good/bad AND/or if you want to do something like logging based on the value only after a certain amount of time passes. Some examples from this page copied below for quick reference - you can do whatever you want in your "_getErrorMessage" function (https://github.com/OfficeDev/office-ui-fabric-react/blob/master/packages/office-ui-fabric-react/src/components/TextField/examples/TextField.ErrorMessage.Example.tsx):
<TextField
label="Deferred string-based validation"
placeholder="Validates after user stops typing for 2 seconds"
onGetErrorMessage={this._getErrorMessage}
deferredValidationTime={2000}
/>
<TextField
label="Validates only on focus and blur"
placeholder="Validates only on input focus and blur"
onGetErrorMessage={this._getErrorMessage}
validateOnFocusIn
validateOnFocusOut
/>

React form input won't let me change value

I have a component in a React class in my Laravel project which is a simple form with one input field. It houses a phone number which I have retrieved from the database and passed back through the reducer and into the component as a prop. Using this, I have passed it through to the module as a prop which then populates the field with the currently saved value:
<OutOfOfficeContactNumberForm
show={props.showOutOfOffice}
value={props.outOfOfficeNumber}
handleChange={console.log("changed")}
/>
I have a handleChange on here which is supposed to fire a console log, but it only ever displays on page load. Here is my form module class:
class OutOfOfficeContactNumberForm extends React.Component {
render() {
const { show, value, handleChange } = this.props;
if(!show) return null;
return (
<div>
<p>
Please supply an Out of Office contact number to continue.
</p>
<InputGroup layout="inline">
<Label layout="inline" required={true}>Out of Office Contact Number</Label>
<Input onChange={handleChange} value={value} layout="inline" id="out-of-office-number" name="out_of_office_contact_number" />
</InputGroup>
</div>
);
}
}
export default (CSSModules(OutOfOfficeContactNumberForm, style));
The form is embedded in my parent component, as follows:
return (
<SectionCategoriesSettingsForm
isSubmitting={this.state.isSubmitting}
page={this.props.page}
show={this.props.show}
categories={this.props.categories}
submitSectionCategoriesSettings={this._submit.bind(this, 'add')}
updateSelectedCategories={this._updateSelectedCategories.bind(this)}
selectedCategoryIds={this.state.selectedCategoryIds}
storedUserCategories={this.props.selectedCategories}
outOfOfficeNumber={this.state.outOfOfficeNumber}
onUpdateContactNumber={this._updateContactNumber.bind(this)}
/>
);
In my componentWillReceiveProps() function, I set the state as follows:
if (nextProps.selectedCategories && nextProps.selectedCategories.length > 0) {
this.setState({
outOfOfficeNumber: nextProps.outOfOfficeNumber,
selectedCategoryIds: nextProps.selectedCategories.map(c => c.id)
});
}
I'm pretty sure the reason it's not changing is because it's pre-loaded from the state which doesn't change - but if I cannot edit the field how can I get it to register a change?
EDIT: Just to clarify there are also checkboxes in this form for the user to change their preferences, and the data retrieved for them is set the same way but I am able to check and uncheck those no problem
Changes:
1- onChange expect a function and you are assigning a value that's why, put the console statement inside a function and pass that function toOutOfOfficeContactNumberForm component , like this:
handleChange={() => console.log("changed")}
2- You are using controlled component (using the value property), so you need to update the value inside onChange function otherwise it will not allow you to change means input values will not be not reflect in ui.
Check example:
class App extends React.Component {
state = {
input1: '',
input2: '',
}
onChange = (e) => this.setState({ input2: e.target.value })
render() {
return(
<div>
Without updating value inside onChange
<input value={this.state.input1} onChange={console.log('value')} />
<br />
Updating value in onChange
<input value={this.state.input2} onChange={this.onChange} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />
I think the best way is when you get data from database put it to state and pass the state to input and remember if you want to see input changes in typing, use a function to handle the change and that function should change state value.
class payloadcontainer extends Component {
constructor(props) {
super(props)
this.state = {
number:1
}
}
render() {
return (
<div>
<input value={this.state.number} onChange={(e)=>this.setState({number:e.target.value})}></input>
<button onClick={()=>this.props.buyCake(this.state.number)}><h3>buy {this.state.number} cake </h3></button>
</div>
)
}
}

Posting changed form field values in React

I have a form which is passed a value through props, and submits to an endpoint to update a users information. However, I'm unable to send an edited value of the text input field, as its state needs to be managed and updated when a user changes its value, but having trouble setting/updating the state of the input when the user changes the value, allowing a different value to be posted.
class DisplayNameModal extends React.Component {
constructor (props){
super(props)
this.state = {
displayName: this.props.displayName,
email: this.props.email.split('#')[0]
}
this.updateDisplayName = this.updateDisplayName.bind(this)
}
updateDisplayName () {
const email = this.props.email
const displayName = this.state.displayName
const user = {
email,
displayName
}
superagent
.put('/api/user')
.send({user})
.then(this.closeModal)
}
handleDisplayNameChange = e => this.setState({ displayName: e.target.value })
render (props) {
const {contentStrings} = this.props.config
return (
<div>
{ !this.props.displayNameModalActive &&
<div className='display-name-container' style={{ backgroundImage: `url(${this.props.bgImgUrl})` }}>
<div className='display-name-content'>
<h2 className='heading'>{contentStrings.displayNameModal.heading}</h2>
<p>{contentStrings.displayNameModal.subHeading}</p>
<input type="text"
defaultValue={this.state.displayName}
onChange={this.handleDisplayNameChange}
minLength="3"
maxLength="15"/>
<button
type='submit'
onClick={this.updateDisplayName}
className='btn btn--primary btn--md'>
<span>{contentStrings.displayNameModal.button}</span>
</button>
<p className='cancel'>{contentStrings.displayNameModal.cancel}</p>
</div>
</div>
}
</div>
)
}
}
export default DisplayNameModal
I think you need an onChange on your <input /> to update displayName on component state.
handleDisplayNameChange = e => this.setState({ displayName: e.target.value });
<input type="text"
value={this.state.displayName}
minLength="3"
maxLength="15"
onChange={this.handleDisplayNameChange}
/>
and instead of defaultValue, use value to make it a controlled input
So then in your updateDisplayName, you would use this.state.displayName instead of this.props.displayName. The prop is just being used to set the initial component state value, allowing it to be edited.
onChange event, call a method, and inside it use this.setState to set the changed text to state as you type in Input box.
On submit, use the updated State value to pass it to the API.
In this way, you can maintain updated value in local state.
You are using uncontrolled input element, ie React doesnt know, wats going on with ur input element.
In order, for React to know about it, it hould be made controlled component.
This can be done by connecting it value to the the state of the component,
Check example below.
This means, that at any time, react will know, wat is the value of the input element
Controlled Components
In HTML, form elements such as , , and typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState().
We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component”.
For example, if we want to make the previous example log the name when it is submitted, we can write the form as a controlled component:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Since the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth. Since handleChange runs on every keystroke to update the React state, the displayed value will update as the user types.
With a controlled component, every state mutation will have an associated handler function. This makes it straightforward to modify or validate user input. For example, if we wanted to enforce that names are written with all uppercase letters, we could write handleChange as:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}

Props not updated

I'm a new user of React and I try to dispatch a modification from my redux store into my components through a container component and props.
My problem is at the end, the data isn't updated. I tested and I figured out that in a Board component, I got the correct edited state (I edit a module's name in this.state.mlodules[1].name) but this value isn't sent in the Bloc component. Here is the render function of my Board component:
render() {
const modules = this.state.modules.map((module) => (
<Draggable key={module._id} x={module.position.x} y={module.position.y} inside={this.state.inside}>
<Bloc module={module} editModule={this.props.onModuleEdited(module._id)}/>
</Draggable>
));
return (
<div className="board"
onMouseLeave={this.mouseLeave}
onMouseEnter={this.mouseEnter}>
{modules}
</div>
);
}
And here is the render function of my Bloc component (I'm using a BlueprintJS editable text):
render() {
return (
<div className="pt-card pt-elevation-3 bloc">
<span onMouseDown={this.preventDrag}>
<EditableText
className="name"
defaultValue={this.props.module.name}
onChange={this.nameChanged}
/>
</span>
</div>
);
}
Any ideas ?
Thanks !
as i mentioned in my comment, you are assigning a defaultValue and not assigning a value prop.
according to their source code on line #77 you can see that there's a value prop.
Edit: As you can see in the docs, defaultValue is uncontrolled input where's value is a controlled input
I think, issue is defaultText. defaultText will assign the default text only on initial rendering it will not update the value. So instead of that assign the props value to value property.
Like this:
value = {this.props.module.name}
Note: But it will make the field read-only, if you don't update the props value (state of parent component) in onChange method.
Check this example, when you click on text 'Click Me' it will update the state value but text of input field will not change:
class App extends React.Component{
constructor(){
super();
this.state = {a:'hello'}
}
click(){
this.setState({a: 'world'},() => {
console.log('updated value: ', this.state.a)
})
}
render(){
return(
<div>
<Child value={this.state.a}/>
<p onClick={() => this.click()}>Click Me</p>
</div>
)
}
}
class Child extends React.Component{
render(){
console.log('this.props.value', this.props.value)
return(
<input defaultValue={this.props.value}/>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>

How do I reset the defaultValue for a React input

I have a set of React input elements that have a defaultValue set. The values are updated with an onBlur event.
I also have another action on the page that updates all values in these input elements. Is there a way to force react to render the new defaulValues when this happens?
I can't easily use onChange since it would trigger a premature rerender (The inputs contain a display order value and a premature rerender would move them).
I could create a duplicate state, one for the real values that is only updated with onBlur and one to update the value in the input element while it is being edited. This would be far from ideal. It would be so much simpler to just reset the default values.
As mentioned in https://stackoverflow.com/a/21750576/275501, you can assign a key to the outer element of your rendered component, controlled by state. This means you have a "switch" to completely reset the component because React considers a new key to indicate an entirely new element.
e.g.
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
key: Date.now(),
counter: 0
};
}
updateCounter() {
this.setState( { counter: this.state.counter + 1 } );
}
updateCounterAndReset() {
this.updateCounter();
this.setState( { key: Date.now() } );
}
render() { return (
<div key={this.state.key}>
<p>
Input with default value:
<input type="text" defaultValue={Date.now()} />
</p>
<p>
Counter: {this.state.counter}
<button onClick={this.updateCounter.bind( this )}>Update counter</button>
<button onClick={this.updateCounterAndReset.bind( this )}>Update counter AND reset component</button>
</p>
</div>
); }
}
ReactDOM.render( <MyComponent />, document.querySelector( "#container" ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container" />
I've solved this by using both onBlur and onChange and only keeping track of the currently active input element in the state.
If there is a way to reset the module so that it re-displays the new default values then I'll mark that as correct.
state = {
inFocusIndex: null,
inFocusDisplayOrder: 0,
};
onOrderBlur() {
const productRow = this.props.products[this.state.inFocusIndex];
const oldDisplayORder = productRow.displayOrder;
// This can change all the display order values in the products array
this.props.updateDisplayOrder(
this.props.groupId,
productRow.productGroupLinkId,
oldDisplayORder,
this.state.inFocusDisplayOrder
);
this.setState({ inFocusIndex: null });
}
onOrderChanged(index, event) {
this.setState({
inFocusIndex: index,
inFocusDisplayOrder: event.target.value,
});
}
In the render function:
{this.props.products.map((row, index) => {
return (
<input
type="text"
value={index === this.state.inFocusIndex ? this.state.inFocusDisplayOrder : row.displayOrder}
className={styles.displayOrder}
onChange={this.onOrderChanged.bind(this, index)}
onBlur={this.onOrderBlur.bind(this)} />
);
})}

Resources