Setting the defultValue of TextField component of Material UI in React - reactjs

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>
);
}
}

Related

How to get children values on antd's form submit?

I made a custom component called TagsInput, and added it to a form. My problem is, when the form is submitted, the value for field tags is always undefined, because the Form.Item cannot retrieve the value from the TagsInput child. This is usually not a problem when children of Form.Item are antd components.
export default function NewProjectForm(props) {
return (
<Form layout="vertical" onFinish={props.onSubmit} id="newProjectForm">
<Form.Item label="Tags" name="tags">
<TagsInput />
</Form.Item>
</Form>
);
}
TagsInput is a class component that has its value within its state:
class TagsInput extends React.Component{
constructor(props){
super(props);
this.state = {
value: []
}
}
render(){
return(<div></div>)
}
}
I dont want to elevate the value field from TagsInput state to NewProjectForm because it is not a class component... I would be doing it just for the sake of solving this issue. I am asking this question because I think there is a cleaner way of solving this.
How can I make NewProjectForm retrieve the value of TagsInput from within its state?
Is there any function I can pass to TagsInput as a property, so that the form asks the component for a value when it is submitted?
In order to achive what you want, you need to handle the state of the form (and its components) at the parent component (NewProjectForm).
If you are willing to keep that component a function component, you can use React Hooks and define a state with useState like this:
const [tagValue, setTagValue] = useState([]);
While tagValue is the value you will get from the child TagsInput, and the setTagValue is the function that can set that value. You need to pass setTagValue as a function prop to TagsInput and use it on change. It will look something like this:
export default function NewProjectForm(props) {
const [tagValue, setTagValue] = useState([]);
return (
<Form layout="vertical" onFinish={props.onSubmit} id="newProjectForm">
<Form.Item label="Tags" name="tags">
<TagsInput setTagValue={(val)=>setTagValue(val)} />
</Form.Item>
</Form>
);
}
class TagsInput extends React.Component{
constructor(props){
super(props);
this.state = {
value: []
}
}
.........
.........
onChangeInput(val){
this.props.setTagValue(val);
}
.........
.........
render(){
return(<div></div>)
}
}
EDIT
After the clarification in the comments, you actually need to follow the steps of using Customized Form Controls, As described here.
You need to pass an object with value and onChange that changes the value. Then, the Form component should manage to get that value automatically.
So I guess it should look something like that:
const TagsInput= ({ value = {}, onChange }) => {
const triggerChange = newValue => {
if (onChange) {
onChange(newValue);
}
};
return(<div></div>)
};

Set value of Reactstrap's Input to empty string

How do I set the value of a Reactstrap Input to be empty? I am using code like this.
handleChange = (event) => {
this.setState({message: event.target.value});
};
< Input onChange = {this.handleChange}/>
Also, what is the best practice for getting the value of an input: refs or onChange?
You gotta set a value property for your input which holds the value of your input, so your input looks like this:
<Input value={this.state.message} onChange={this.handleChange} />
and then when you want to clear its value, you just do this:
this.setState({message: ''})
and for your other question, the answer is that the Refs provide a way to access DOM nodes or React elements created in the render method, according to React documentation You can use Refs when:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
and you have to AVOID using refs for anything that can be done declaratively.
Then, here we just use onChange because we don't simply need Refs!
You need to assign state value to your input field.
public class myApp() extends React.Component {
constructor(props){
super(props);
this.state = {
message: ''
};
}
handleChange = (event) => {
this.setState({message: event.target.value});
};
render() {
return (
<Input value={this.state.message} onChange={this.handleChange} />
);
}
}

Suppress onChange and only use onBlur to a React input component

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}
/>
}
}

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