Suppress onChange and only use onBlur to a React input component - reactjs

I have a component called Test that receives 2 props: CurrentPage and OnPageRequest: (currentPage: number) => void
When the parent component uses Test, it sets the Current page number in its state. As you update, the input field below, that value gets fed back up to the parent component via the OnPageRequest as:
OnPageRequest(e.target.value);
wherein return, the parent component updates its current page number in the state.
Test component:
const handlePageNumberBlur = (e) => {
OnPageRequest(e.target.value);
}
render() {
return (
<input
title="Current Page"
onBlur={handlePageNumberBlur}
aria-label="Current Page"
min="1"
max={TotalPages}
placeholder="1"
type="number"
value={this.props.CurrentPage} />
)
}
I'm trying to write Test in such a way so that it only fires back the newly inputted value only when the onBlur event is triggered. But I've learned that with the above code, it never updates the value. It only does it if I have an onChange event where I do: OnPageRequest(e.target.value);
How can I get it to change only when onBlur fires?
SAMPLE: https://codesandbox.io/s/kumuc

Try using defaultValue instead of value. The defaultValue prop only sets the value to the input on initial render and you won´t need to keep it updated if the value of the prop changes.
<input
{ ... }
defaultValue={this.prop.CurrentPage}
/>
But this can also be an issue if you set CurrentPage after the input has rendered, which depends on where you get your initial value for the prop.
If you need to keep the input updated after the first render, you can update its value using Refs and the componentDidUpdate lifecycle method.
class TestComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidUpdate(prevProps) {
if (this.inputRef.current.value != this.props.CurrentPage) {
this.inputRef.current.value = this.props.CurrentPage;
}
}
/* ... */
render() {
return <input
{ ... }
defaultValue={this.props.CurrentPage}
ref={this.inputRef}
/>
}
}

Related

TextField default value from parent not rendering on child

I'm working on a form with Reactjs that gets some defaultValues from the parent component.
The problem is, the parent component set states of the values with a axios post and pass those values to the child as props. I can print those values on the child component with console.log but if I try to put those values on defaultValues on the TextFields I got a empty form, none of the values is rendered on the form.
Parent component:
export default class Parent extends Component {
constructor(props){
super(props);
this.state={
somevalue: '',
}
}
componentDidMount(){
this.getData();
}
getData = async () => {
await api.post('/getValue')
.then((res) => {
this.setState({
someValue: res.data;
})
}).catch((err) => {
console.log("Error: ", err);
})
}
render(){
return(
<Child value={this.state.someValue}/>
)}
}
Child component
export default function Child(props) {
console.log(props.value); // THIS LOG PRINT THE VALUE PROPERLY
return(
<TextField defaultValue={props.value}/>
)
}
This is basically my code structure, but it's not working. TextField still empty after this.
The property defaultValue is only used on the initial render. If you'll inspect your code you'll see that before the console.log outputs the value it will first output undefined. You can either change it to a controlled component by changing defaultValue to value. This way the value will display, but you'll need to add an onChange handler for the changes to the value.
function Child(props) {
// Using the value prop your value will display, but you will also have to pass an onChange handler to update the state in the parent
return <TextField value={props.value} />;
}
Or you can wait until the value is available before rendering your component
const { someValue } = this.state;
if (!someValue) {
return "loading the data";
}
return <Child value={someValue} />;
It depends on the exact situation what solution will be better. But I think it's likely you'll want to update the value in the input and do something with it, so I would go with the first situation.

React: Get state of children component component in parent

I have this container where and is not placed in the same level. How can I get the state of the Form when I click on the button (which is placed on the parent) ?
I've created a demo to address my issue.
https://codesandbox.io/s/kmqw47p8x7
class App extends React.Component {
constructor(props) {
super(props);
}
save = () => {
alert("how to get state of Form?");
//fire api call
};
render() {
return (
<div>
<Form />
<button onClick={this.save}>save</button>
</div>
);
}
}
One thing I don't want to do is sync the state for onChange event, because within Form there might be another Form.
To access a child instance from parent, your need to know about ref:
First, add formRef at top your App class:
formRef = React.createRef();
Then in App render, pass ref prop to your Form tag:
<Form ref={this.formRef} />
Finaly, get state from child form:
save = () => {
alert("how to get state of Form?");
const form = this.formRef.current;
console.log(form.state)
};
Checkout demo here
ideally, your form submit action belongs to the Form component
You can put button inside your From component and pass a submit callback to the form.
class App extends React.Component {
constructor(props) {
super(props);
}
save = (data) => {
// data is passed by Form component
alert("how to get state of Form?");
//fire api call
};
render() {
return (
<div>
<Form onFormSubmit={this.save} />
</div>
);
}
}
you can write the code like this
https://codesandbox.io/s/23o469kyx0
As it was mentioned, a ref can be used to get stateful component instance and access the state, but this breaks encapsulation:
<Form ref={this.formRef}/>
A more preferable way is to refactor Form to handle this case, i.e. accept onChange callback prop that would be triggered on form state changes:
<Form onChange={this.onFormChange}/>
One thing I don't want to do is sync the state for onChange event, because within Form there might be another Form.
Forms will need to handle this any way; it would be impossible to reach nested form with a ref from a grandparent. This could be the case for lifting the state up.
E.g. in parent component:
state = {
formState: {}
};
onFormChange = (formState) => {
this.setState(state => ({
formState: { ...state.formState, ...formState }
}));
}
render() {
return (
<Form state={this.state.formState} onChange={this.onFormChange} />
);
}
In form component:
handleChange = e =>
this.props.onChange({
[e.target.name]: e.target.value
});
render() {
return (
<input
onChange={this.handleChange}
name="firstName"
value={this.props.state.firstName}
/>
);
}
Here is a demo.

Setting the defultValue of TextField component of Material UI in React

I am developing a React JS application. What I am doing now is I am fetching data asynchronously from the server and render the values in the TextField. I know how to do it and my approach is working as well. But, I just do not like my current approach.
This is the working approach
class TestComponent extends React.Component {
constructor(props)
{
super(props);
this.fetchName = this.fetchName.bind(this);
this.handleChange = this.handleChange.bind(this);
this.state = {
name : ''
}
}
fetchName()
{
//get the name asynchronously from the server. I am using Axios
this.setState({ name : nameValueFromTheServer })
}
handleChange(e)
{
this.setState({ name : e.target.value })
}
render()
{
return (
<MuiThemeProvider>
<div>
<TextField onChange={this.handleChange} label="Name" value={this.state.name} />
</div>
</MuiThemeProvider>
)
}
}
The thing I do not like about above approach is that I have to implement handleChange method to retrieve the value of the input value. If I do not implement it, I cannot change the name input value because the state value is not changed. To retrieve a simple input value, I have to write extra lines of code. In jQuery, I just retrieve the input value like this, $(selector).val(). Without implementing the handleChange method, I can use the ref like this.
<TextField inputRedf={(input) => this.tfName = input } label="Name" />
Then I retrieve the value dynamically like this.
this.tfName.value
The problem with using the reference is setting the default value which is fetched asynchronously from the server.
I can set the default value like this to the TextField
<TextField inputRedf={(input) => this.tfName = input } defaultValue={this.state.name} label="Name" />
Pay attention to the defaultValue attribute in the above code. When I set the value using defaultValue, the initial value of the name state will be set to the text field. But we can still change the input value of the name typing in from the UI even if we do not implement the handleChange event. But the problem is that I am getting the data asynchronously and setting the this.state.name dynamically.
So in the component constructor, the name state is empty. Then I set it in the callback of the asynchronous call. But the defaultValue can be set only once. So the input field is always showing empty. What I like to know is, how can I set the defaultValue of the TextField dynamically? The reason I am using ref is that I do not want to implement the onChange event as well to track state the reset the input value. If I could set the defaultValue dynamically, I will just the value dynamically in the callback of the asynchronous call. What is the best solution to simplify this scenario?
You don't need default value actually. If you want to avoid using onChange handler you can imperatively set input value in async callback:
class TestComponent extends React.Component {
constructor(props) {
super(props);
this.fetchName = this.fetchName.bind(this);
this.handleChange = this.handleChange.bind(this);
}
fetchName() {
if(this.tfName.value == '') {
// only update if user have not changed the input value.
this.tfName.value = nameValueFromTheServer;
}
}
render() {
return (
<MuiThemeProvider>
<div>
<TextField
inputRef={(input) => this.tfName = input }
label="Name"/>
</div>
</MuiThemeProvider>
);
}
}

react-select does not clear value when redux-form is reset

I have a stateless React function to render a react-select Select to a form. Everything else works nicely with redux-form except when redux-form is reset. I need to reset the form manually after successful post.
onChange and onBlur change the redux-form value correctly when Select has a value change. When I reset the redux-form, the redux-form value is cleared but the Select will have the old value.
function SelectInput(props) {
const { input, options, label } = props;
const { onChange, onBlur } = input;
const handleChange = ({ value }) => {
onChange(value);
};
const handleBlur = ({ value }) => {
onBlur(value);
};
return (
<FormField {...props}>
<Select placeholder={label} options={options} onChange={handleChange} onBlur={handleBlur} />
</FormField>
);
}
I converted the SelectInput to React.PureComponent, and added the value as a state inside the component and looked for when the Component received new props:
constructor(props) {
super(props);
this.state = {value: ''}
}
componentWillReceiveProps(nextProps){
this.setState({value: nextprops.input.value})
}
<Select value={this.state.value} placeholder={label} options={options} onChange={handleChange} onBlur={handleBlur} />
With this Select was not able to show the value at all.
The problem is that how I can update the Select to show empty value when redux-form that this field is part of is reset? Redux-form resets the value corretly inside the redux state and if I try to submit the form, validation notices that that Select has empty value. The Select will however display the old value so that user thinks that there is a value selected.
Reset is done by dispatching reset in the actual redux-form component. Redux devtools show that fields are reset and the redux state is cleared from all the value, Select component just won't update the DISPLAYED value to empty.
const afterSubmit = (result, dispatch) =>
dispatch(reset('datainputform'));
export default reduxForm({
form: 'datainputform',
onSubmitSuccess: afterSubmit,
})(DataInputForm);
Versions I use:
react-select#v2.0.0-beta.6
redux-form#7.3.0
You can also set a key at the form level itself. The key will take a unique value that you can store in the component state. This unique value will be updated every time reset is hit.
state = {
unique_key: ''
}
// this is the onClick handler for reset button on the form
onResetForm = () => {
reset_val = Date.now();
this.props.reset();
this.setState({unique_key: reset_val});
}
<Form actions={action_func}, key={this.state.unique_key}/>
Now whenever reset is clicked, the handler will update the unique_key. This will result in re-rendering the Form with the default values. The handler also calls the form reset function to clear the redux.
Got it working. Problem was handling the Select null value. Changed stateless function to PureComponent, added the value to state.
constructor(props) {
super(props);
this.state = { value: '' };
}
Redux-form changes the react-select value by sending new props. So added
componentWillReceiveProps(nextProps) {
if (nextProps.input.value === '') {
this.setState({ value: '' });
}
}
Added setState to handleChange:
handleChange = (data) => {
const value = data === null ? '' : data;
this.setState({ value });
this.props.input.onChange(data.value);
};
And then added the value prop.
<Select value={this.state.value}...

How to get value datapicker in react toobox custom?

How can I get the value of datapicker in react toobox?
I am using custom components.
I am using 2 components first one is called InputDateCustom.js with the code below:
import DatePicker from 'react-toolbox/lib/date_picker/DatePicker';
import React, { Component } from 'react';
const datetime = new Date(2015, 10, 16);
datetime.setHours(17);
datetime.setMinutes(28);
export default class InputDateCustomizado extends Component{
state = {date2: datetime};
handleChange = (item, value) => {
console.log(item+" - "+value)
this.setState({...this.state, [item]: value});
};
render() {
return (
<div>
<DatePicker
label={this.props.label}
locale={localeExample}
name={this.props.name}
required={this.props.required}
onChange={this.handleChange.bind(this, 'date1')}
value={this.state.date1}
/>
</div>
);
}
}
Another component is called Cadastro.js that contains the following logic:
constructor(props) {
super(props);
this.state = {msg: '', fim_vigencia:'', nome:''}
this.setNome = this.setNome.bind(this)
this.setFimVigencia = this.setFimVigencia.bind(this)
}
setFimVigencia(evento){
console.log("date")
this.setState({fim_vigencia:evento.target.value});
}
InputDateCustomizado
id="fim_vigencia"
label="Fim"
name="fim_vigencia"
value = {this.state.fim_vigencia}
onSubmit = {this.setFimVigencia}
/>
Get the value in an onChange event or using the value prop. Doc examples: http://react-toolbox.com/#/components/date_picker
<DatePicker label='Birthdate' onChange={this.handleChange.bind(this, 'date1')} value={this.state.date1} />
You can get access to the value in the handleChange event allowing you to update your state with the currently selected date.
EDIT: Ah okay I think I understand what you are asking now. You have wrapped DatePicker with your own component and now you want to get the DatePicker value through the Cadastro.js component.
You need to create a method in the Cadastro.js that accepts state changes from the InputDateCustomizado component and then pass that function as a prop to the InputDateCustomizado component. In the InputDateCustomizado when the state changes, call the passed in function and it should update the state in the parent component. Then you will always have the datepicker value in the parent component.
It looks like you are almost there. You need to add an updateState function to the Cadastro.js component. In the InputDateCustomizado component handleChange event, you need to call this.props.updateState and pass in the new value.
In Cadastro.js
updateState = (data) => {
this.setState({
date: data.data //set your state here to the date
})
}
In InputDateCustomizado
handleChange = (item, value) => {
console.log(item+" - "+value)
this.setState({...this.state, [item]: value});
this.props.updateState(this.state);
};

Resources