Let me start off by saying that I am brand new to React, with that I was given a problem to solve which consists of clearing the inputs from multiple input fields that are contained in a separate text input component. My path toward a possible solution has been attempting to set the values of said input fields to an empty string using an onClick event in the App.js component. So far I have been able to clear the text field that stores the user inputs, however I cannot seem to figure out how to also clear the input fields as well. Thanks in advance to those taking time to lend their knowledge.
Here is the App.js code:
import { useEffect, useRef, useState } from 'react';
import './App.css';
import LabeledTextInput from './components/LabeledTextInput';
function App() {
const data = useRef(document.getElementById('FIELD_555'));
const [ state, setState ] = useState({});
const handleUpdate = (key, value) => {
setState(prevState => ({
...prevState,
[key]: value
}));
};
useEffect(() => {
data.current.value = JSON.stringify(state);
}, [state]);
return (
<div>
<LabeledTextInput label='Name' handleUpdate={handleUpdate} />
<LabeledTextInput label='Title' handleUpdate={handleUpdate} />
<LabeledTextInput label='Company' handleUpdate={handleUpdate} />
<button onClick={() => setState('')}>Clear Inputs</button>
</div>
);
}
export default App;
And then the LabeledTextInput.js component:
import { useState } from 'react';
const LabeledTextInput = ({ label, handleUpdate }) => {
const [ state, setState ] = useState('');
return (
<div>
<label>{label}</label>
<input
type='text'
name={label.toLowerCase()}
value={state}
onChange={e => setState(e.target.value)}
onBlur={e => handleUpdate(e.target.name, e.target.value)} />
</div>
);
};
export default LabeledTextInput;
CodePudding user response:
This is happening because you are maintaining two states. On the button click you clear the state in App
component, but all your LabeledTextInput
are maintaining their own state which you haven't reset/cleared yet.
The simplest approach here could be to have a Single Source Of Truth for your input fields, i.e to share state between all related fields and functions, it would allow you to manage them well. Something like this:
import { useEffect, useRef, useState } from 'react';
import './App.css';
import LabeledTextInput from './components/LabeledTextInput';
function App() {
const data = useRef(document.getElementById('FIELD_555')); // no idea what's this doing here
const [ state, setState ] = useState({name: '', title: '', company: ''});
const handleUpdate = (key, value) => {
setState(prevState => ({
...prevState,
[key]: value
}));
};
return (
<div>
<LabeledTextInput name='name' value={state.name} label='Name' handleUpdate={handleUpdate} />
<LabeledTextInput name='title' value={state.title} label='Title' handleUpdate={handleUpdate} />
<LabeledTextInput name='company' value={state.company} label='Company' handleUpdate={handleUpdate} />
<button onClick={() => setState({name: '', title: '', company: ''})}>Clear Inputs</button>
</div>
);
}
export default App;
And your LabeledTextInput will be like this:
const LabeledTextInput = ({ name, value, label, handleUpdate }) => {
return (
<div>
<label>{label}</label>
<input
type='text'
name={name}
value={value}
onChange={e => handleUpdate(name, e.target.value)}
onBlur={e => handleUpdate(e.target.name, e.target.value)} /> // you can decide later whether or not to keep onBlur event, or to rename handleUpdate function
</div>
);
};
export default LabeledTextInput;
CodePudding user response:
The issue with your code is that you are maintaining two different states. One in parent component and another one in child component. And hence when you type in, the input is stored in child components state, and when you focus out, the handleUpdate changes the parent component's state. Once that's done, you are clearing the parent's component state by calling setState(''), but that didn't updated the child's state. And that's the issue. State of a component should be maintained at a single place to get rid of such situations.
One way of doing it is by passing the state's value to child's component along with the setter methods for that state.
function App() {
const data = useRef(document.getElementById('FIELD_555'));
const [ state, setState ] = useState({});
const handleUpdate = (key, value) => {
setState(prevState => ({
...prevState,
[key]: value
}));
};
useEffect(() => {
data.current.value = JSON.stringify(state);
}, [state]);
return (
<div>
<LabeledTextInput label='Name' handleUpdate={handleUpdate} value={state?.name} key="name"/>
<LabeledTextInput label='Title' handleUpdate={handleUpdate} value={state?.title} key="title"/>
<LabeledTextInput label='Company' handleUpdate={handleUpdate} value={state?.company} key="company"/>
<button onClick={() => setState({})}>Clear Inputs</button>
</div>
);
}
export default App;
And then the LabeledTextInput.js component:
import { useState } from 'react';
const LabeledTextInput = ({ label, handleUpdate, key, value }) => {
return (
<div>
<label>{label}</label>
<input
type='text'
name={key}
value={value}
onChange={e => handleUpdate(e.target.name, e.target.value)} />
</div>
);
};
export default LabeledTextInput;