We currently have a component <RenderDropdown /> which renders a material-ui <Select /> element. This takes options as props and is wrapped in a react-final-form <Field />.
Our issue is when we load the initialValues props of the <Form />, there are a few cases where the selected dropdown value no longer exists in the dropdown, so we want to set the dropdown value to null. We do that within the <RenderDropdown />:
componentDidMount() {
this.clearInputIfValueDoesntExistInOptions();
}
componentDidUpdate() {
this.clearInputIfValueDoesntExistInOptions();
}
clearInputIfValueDoesntExistInOptions() {
const {
input: { value, onChange },
options
} = this.props;
// Clear input value if it doen't exist in options
if (
value &&
!options.some((option) => option.key === value || option.value === value)
) {
console.log(
"clearInputIfValueDoesntExistInOptions",
"Setting value to null as the selected value does not exist in the current dropdown's options"
);
// If I use input.onChange, it does not update the final form state to null immediately.
onChange(null);
}
}
This does set the value to null, but the form state does not update immediately, so the UI does not update either. It only updates once we change another field value or if we submit the form.
I've made a quick demo that simulates our issue:
https://codesandbox.io/s/react-final-form-inputonchange-null-k7poj
Thanks.
Related
I was going through fabric UI TextField documentation(https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield ) . I tried few examples of TextField with the following two handlers:-
onChange
onGetErrorMessage.
I tried few experiments with these two handlers (https://codepen.io/nishchay271095/pen/rNyPKYY?editors=1011 ):-
Case 1. When using both the handlers without any extra properties of TextField, both handlers(onChange and onGetErrorMessage) are called.
<TextField
label="Controlled TextField"
onChange={onChangeSecondTextFieldValue}
onGetErrorMessage = {onGetErrorMessageHandler}
/>
Case 2. When using both handlers with "defaultValue" property, both handlers(onChange and onGetErrorMessage) are called.
<TextField
label="Controlled TextField"
defaultValue = "hello"
onChange={onChangeSecondTextFieldValue}
onGetErrorMessage = {onGetErrorMessageHandler}
/>
Case 3. When using both handlers with "value" property of TextField, only onChange() handler was called.
<TextField
label="Controlled TextField"
value="hello"
onChange={onChangeSecondTextFieldValue}
onGetErrorMessage = {onGetErrorMessageHandler}
/>
Now, I'm having following doubts:-
Why onGetErrorMessage() is not being called in case 3?
How value and defaultValue properties affect the behavior of these two handlers(onChange and onGetErrorMessage)?
Why onGetErrorMessage() is not being called in case 3?
When you have Uncontrolled Component there is no reason to use value property and onChange function, because TextField Component treats defaultValue as initial and it creates local state variable uncontrolledValue which interacts like when you have value prop.
So, in your case you have Uncontrolled Component and value property is excess.
Uncontrolled Component:
<TextField
defaultValue="Hello"
onGetErrorMessage={value => {
if (value === 'Foo') {
return 'Foo is not valid value';
}
}}
/>
Controlled Component
const [inputValue, setInputValue] = useState('Hello');
<TextField
value={inputValue}
onChange={(_, newValue) => {
if(newValue || newValue === '') {
setInputValue(newValue)
}
}}
onGetErrorMessage={value => {
if (value === 'Foo') {
return 'Foo is not valid value';
}
}}
/>
How value and defaultValue properties affect the behavior of these two handlers(onChange and onGetErrorMessage)?
Answer lies in documentation:
defaultValue:
Default value of the text field. Only provide this if the text field is an uncontrolled component; otherwise, use the value property.
value:
Current value of the text field. Only provide this if the text field is a controlled component where you are maintaining its current state; otherwise, use the defaultValue property.
Full working example Codepen.
I'm using materiel UI form component in a modal.
This modal can be opened for add or edit an item, so values can be empty or not.
I put default props values in a state but this is always empty and never get previous values...
Here is my code :
const Comp = (props) => {
const { edit, values } = props // edit props for editing user
// values is :
{
prenom: 'name',
nom: 'name'
}
// ...
const [nom, setNom] = React.useState(edit ? values.nom : '')
const [prenom, setPrenom] = React.useState(edit ? values.prenom : '')
// ...
return (
<form>
<TextField
id="prenom"
value={prenom}
label="Prénom"
variant="outlined"
onChange={(event) => setPrenom(event.target.value)}
/>
<TextField
id="nom"
value={nom}
label="Nom"
variant="outlined"
onChange={(event) => setNom(event.target.value)}
/>
</form>
)
}
Thanks for your help
I'm guessing that you have your Comp used on the parent but not visible till some state changes, something like isDialogOpen. Then once the user wants to edit some object you do something like
setIsDialogOpen(true);
setDialogEditMode(true);
setValuesToEdit({nom: 'Foo', prenom: 'Bar'});
You have to understand that once you use the component (<Comp prop='value' />) React renders it, even that nothing gets to the actual Dom, the Comp function will be called! so at first it's being called with some empty values, then you want to let the user do the editing, you change the parent state. BUT the Comp state is still the state that it was created with (Empty values).
Remember this: useState acts in two ways:
On the first render it returns the value from the given parameter.
Any future renders it ignores the parameter and returns the saved state.
So in order to change the saved state you have to declare a reaction/effect when the props change, you do that with useEffect inside your Comp
for example:
useEffect(() => {
setNom(values.nom);
setPrenom(values.prenom);
}, [values])
const availableFunc = (id) => {
if (Active.length > 0) {
let available = Active.filter((obj) => obj.type === id).map((obj) =>
obj.available !== undefined ? obj.available : ""
);
return available[0];
}
};
All data is stored in Active and the desired value is present in available when I console available[0] then I am getting the correct value but don't know why it's not updating the value in defaultValue={}. Sometime after 5-6 refresh its updated [but I] don't know how!! Again I refresh the page the value goes hidden.
Please team and seniors help me to solve this problem..
<Select
defaultValue={availableFunc(res.eId)}
onChange={(event) =>
handleChangeSelect(
event.target.value,
res.eId,
res.dropDown[0]
)
}
>
{res.vals &&
res.vals.map((res) => {
return (
<MenuItem value={res}>{res}</MenuItem>
);
})}
</Select>
Issue
The defaultValue prop/attribute is just the initial value of the input when the component mounts. It's generally used for uncontrolled inputs, i.e. you aren't storing the input's value in state (updated via the onChange handler). Changes to this prop don't actually change the value being used by the input, you should be seeing a react warning, something to the effect of "Changing an input from uncontrolled to controlled...".
Solution
Since you've attached an onChange handler I'll assume you meant for this to be a controlled input. Use the value prop.
<Select
value={availableFunc(res.eId)}
onChange={(event) =>
handleChangeSelect(
event.target.value,
res.eId,
res.dropDown[0]
)
}
>
...
I am trying to use Semantic UI react for layout. These are the following sample code I am having trouble with onChange. I can check to click the toggle but resets everytime I refresh.
import {
Checkbox
} from 'semantic-ui-react'
onChangeInput(event) {
let name = event.target.name
let value = event.target.value
let talent = this.state.newTalentProfile
talent[name] = value
this.setState({
newTalentProfile: talent
})
}
<Select
name = "willing_to_relocate"
ref = "willing_to_relocate"
defaultValue = {this.props.talent.willing_to_relocate}
onChange = { this.onChangeInput.bind(this)} >
<Option value = ""label = "" / >
<Option value = "YES"label = "YES" / >
<Option value = "NO"label = "NO" / >
</Select>
the below code doesn't work, but the above one works when i make changes it saves it to database
<Checkbox toggle
name = "willing"
ref = "willing"
label = "Willin To Relocate"
onChange = {this.onChangeInput.bind(this)
}
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
onChangeCheckbox = (evt, data) => {
let checked = data.checked
console.log(checked)
}
<Checkbox toggle label='Running discounted price?'
onClick={(evt, data)=>this.onChangeCheckbox(evt, data)}
/>
This should do the trick.
Your code suggests you expect event.target to be a checkbox element, but it is not. If you examine the variable event.target within your browser, you'll notice that it points to a label element, not the checkbox itself. Therefore, I'm guessing that your event.target.value and event.target.label are evaluating to null, causing the rest of your code to not function the way you expect.
There's many ways to get around this. One way is to set a state variable for your component to represent the state of the checkbox:
class TheComponentThatContainsMyCheckbox extends Component {
state = {
textValue: null,
willingToRelocate: true
}
...
Then, you can create a handler that toggles this state when checked:
toggleCheckBox = () => {
const willingToRelocate = !(this.state.willingToRelocate);
this.setState({willingToRelocate});
// Note: if you have other logic here that depends on your newly updated state, you should add in a callback function to setState, since setState is asynchronous
}
Notice that I'm using arrow syntax here, which will automatically bind this for me. Then, hook it up to the onChange event of the Semantic UI Checkbox:
<Checkbox label='Willing to Relocate.' onChange={this.toggleCheckBox}/>
You can now use your this.willingToRelocate state to decide other application logic, passing it up or down using whatever state management pattern you like (state container/store like Redux, React Context, etc.).
I assume your this.onChangeInput handles database updates.
semantic ui react offers a prop checked for Checkbox.
You should have a state {checked: false} in your code, then this.setState whatever in componentDidMount after retrieving the database record.
Pass this.state.checked into checked prop of Checkbox.
I have given someone else's React project which is using the React-Redux-Form (this is not the redux-form) and there is a need to enable a checkbox which is on another React Component based upon a value being entered in a textbox in the React-Redux-Form. By defult the checkbox is disabled and should only be enabled if a value is present.
Searching on the Internet have failed to find an example in which to use. Can anyone help?
Here is a very rough example, and is not intended to run - simply show the flow of how to do it:
The component with the input field might have something like:
//1) this is setting a local state, when the user submits you can send it to redux so that it is available across the app.
this._saveInputToLocal() {
var emailRegEx = /^.+#.+\..+$/i;
this.setState({
email: e.target.value
}, function() {
}.bind(this));
},
//2) when the user clicks submit - send the input to redux
//lets assume this is saved into a property on state called `formInput`:
this._saveInputToRedux() {
const = { dispatch }
dispatch(saveUserInput(this.state.email))
},
render: function(){
return (
<div className={`field email ${css(styles.emailContainer)}`}>
<input
type='email'
placeholder={ 'helloworld#code.com' }
onChange={this._saveEmailToState}
/>
</div>
<button type='submit' onClick={this._saveInputToRedux}>Submit</button>
)
}
});
So you see, you have a few things: a function that updates a local state, and one that handles the submit - where the value of the local state fires and action that will store that value in redux. Don't forget to import connect so that you have dispatch available to you on props, and also don't forget to import your action into the component.
Now, In the other component that has the checkbox:
// 3) assuming you have mapped your redux state to your props you make a function that checks for a valid input value:
this._hasValidValue() {
const { formInput } = this.props
return formInput && formInput !== null && formInput !== undefined && formInput.length > 0
}
//here in the render you display either an enabled or disabled checkbox based on if you have a valid input from the other component. Because this is based on state, this component will re-render when state is updated (goes from invalid to valid or vice versa)
render: function(){
return (
<div className="checkbox">
{ hasValidValue()
? <input type="checkbox" name="some-name" />
: <input type="checkbox" name="some-name" disabled/>
}
)
}
});