I have an input field in React, where I set the global name for my application.
I have the handlers set in the parent component called App.js
....
state = {
name: 'app_x',
isProcessedData: false,
isUploadedFiles: false,
isProcessedDBScan: false
}
setNameHandler = (e) => {
this.setState({name: e.target.value});
}
...
in my child component, I refer to the state and the handler via props
export default function Home(props) {
console.log(props)
return (
<div>
<p>Welcome to our application</p>
<TextField
id="outlined-name"
label="Name"
className={"textfiled"}
value={props.name}
onChange={props.handleChange} //the textfield loses focus after each time the input is changed
margin="normal"
variant="outlined"
/>
</div>
)
}
after each keystroke, the text field, is not focused anymore, because the entire UI is rendered.
I tried using React.useState("app_x")
for testing purposes, which worked fine, as long it's inside the component, but I need to pass down the state of the application to other components.
Is there a way to solve this?
I thought about using refs, and then passing a ref down as a callback instead, but as far as the most use cases I have looked at it's mostly relevant inside the same component?
Related
When I type something in my custom field it loses focus and needs to be clicked again in order to add data, Below is the sandbox link to code. It only happens when I pass error and touched props to check for validation, If I remove that it works perfectly.
https://codesandbox.io/s/formik-example-forked-w0bub?file=/index.js
Browser I'm using is Chrom for windows
The input inside the functional component is recreated every time when there is state changes.
Read this blog post to know more the behaviour of nested functional components
To avoid recreation problem, you can follow the Formik example to create other functional component and use it inside your component.
const MyInput = ({ field, form, ...props }) => {
return <input {...field} {...form} {...props} />;
};
...
<Field
id="email"
name="email"
placeholder="Email"
component={MyInput}
/>
Here is the codesandbox for demo.
I have an input field in react, whose value is initially set by a props received by the component
<EditUserModal
show={this.state.showModal}
close={this.closeModal}
userId={this.state.rowId}
email={this.state.selectedMail}
/>
As you can see it received the value email, and inside the component I can see the valu changing properly
<div class="form-group emailgroup">
<label for="exampleFormControlInput1">Change Email Address</label>
<input
type="email"
class="form-control"
placeholder="name#example.com"
value={this.props.email}
onChange={this.handleChange}
/>
</div>
Problem is, that I add a handleChange event, to be able to modify that input, but it doesnt modify the email, I suppose that its because I should change the props in the parent component, but I dont understand how to do it. Currently my handleChange function looks like this.
handleChange = (evt) => {
this.setState({
email: evt.target.value
});
}
And this is the initial state fo the component
this.state = {
email : ""
};
Obviously since im not calling the state, but the props to asign the value of the field, it doesnt change. But ive tried....
this.state = {
email : props.email
};
And it lets me change it this way, but the problem is that it will render nothing initially in the input value.
Don't assign your props to state, instead call your function from parent and pass state value to child component.
you can refer this for better understanding of parent to child data flow.
How to change the state of a child component from its parent in React
You are passing the wrong value into the component. Current you have this:
email={this.state.selectedEmail}
But when you look at what you're setting in the parent component, it's not 'selectedEmail' but 'email'. Changing the property should fix it:
email={this.state.email}
Then the correct value should be worked on.
I have created a React View, say MyView, which has 2 text inputs whose initial values will be passed by parent read from a DB.
I also want the changed values to be saved back to DB. So, the view is also passed a callback function for the same.
Consider that DB save operation is heavy and you should not do it very frequently. So, I decided to listen to onBlur events instead of onChange events on the input boxes as onChange is invoked on every key stroke.
First Approach:
class MyView extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<input type="url" value={this.props.values.A}
onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
<input type="url" value={this.props.values.B}
onBlur={(evt)=>{this.props.saveValue('B', evt.target.value)}} />
<button type="button" onClick={this.props.resetValues}>Reset</button>
</div>
);
}
}
However, this does not work as React enforces a controlled input (with value attribute) always to be accompanied by an onChange listener.
Second Approach:
So, I tried to make these inputs as uncontrolled. That is, instead of value attribute, used defaultValue.
<input type="url" defaultValue={this.props.values.A}
onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
But this also did not work as on reset/clear button click, although the view was made to re-render but defaultValue does not update once view is created.
Third Approach:
So, I finally added an onChange listener but as no-op.
<input type="url" value={this.props.values.A}
onChange={()=>{console.log('do nothing')}
onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
Again, this did not work as the view re-renders after calling onChange and since value is not reflected in props yet, value seems to reset back to initial on every key stroke.
Fourth Approach:
Last I tried was to maintain a state in component and read value from state and on every onChange save the value back to state. This worked to most extent but whenever there were external changes to props and the view was re-rendered, state did not update. So, I added a getDerivedStateFromProps function to view:
static getDerivedStateFromProps(props, state) {
return props.values;
}
Now, this again did not work. Reason being that this function is invoked even if I temporarily save values to state and the state was reset to initial values in props.
Can some ReactJS expert help me with my use-case?
You will still need onChange to help you set the states of both url input. onBlur is only used to trigger saving, it's 2 different events for different purposes.
Since your A & B values are passed down from parent component. MyView's parent component should pass down this.state.values and the functions to set the state.
Refer to this snippet if everything is in single component. You should be able move handleChange function up to its parent component.
class App extends React.Component {
state = {
values: {
A: '',
B: ''
}
}
handleChange = e => {
this.setState({
values: {
...this.state.values,
[e.target.name]: e.target.value
})
}
handleBlur = e => {
if (e.target.name === 'A') {
alert(`Saving A: ${this.state.values.A}`)
}
if (e.target.name === 'B') {
alert(`Saving B: ${this.state.values.B}`)
}
}
render() {
return (
<div>
<label>Value A</label>
<input
type="url"
name="A"
value={this.state.values.B}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
<label>Value B</label>
<input
type="url"
name="B"
value={this.state.values.A}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('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">
</div>
EDIT: Your fourth approach should work with the following:
static getDerivedStateFromProps(props, state) {
return { values: props.values }
}
constructor(props) {
super(props)
this.state = {
values: props.values
}
}
so basically the this.state.values is the final source of truth. When user types something, you setState in this component and change it. But if props.values changes (from external source), getDerivedStateFromProps will update the values state.
Going by the comments on Liren Yeo's solution, I would handle the props-state reconciliation on componentDidUpdate, where you get both the old state and props. This way you can determine how this.props was updated and act accordingly. When the value in props does not match state nor oldProps, the update is external and you should override the unsaved changes in the state.
The code should look something like this
componentDidUpdate(prevProps) {
if (this.props.values !== prevProps.values && this.props.values !== this.state.values) {
this.setState({values:this.props.values});
}
}
If you go this route, you can also leave the input uncontrolled and update its value through a reference. This solves some unreliability with controlled inputs, like for example, a type='number' returning undefined as its value when you type a decimal comma. You still need to store the value onChange but only save it onBlur and handling the state-prop-dom reconciliation in componentDidUpdate
So with the idea that onChange works, I would recommend you to take a look at this:
https://schier.co/blog/2014/12/08/wait-for-user-to-stop-typing-using-javascript.html
Navigate to the heading: Wait for Typing to Stop
Hope it can somehow lead you to what you want to achieve.
I have some Controlled Component text inputs for a form:
<input type="text" onChange={(e) => this.props.changeBusiness(e)}/>
I also want to render the (above) text input in a separate component - however! It would be nice if I could render it with the COMPLETED text input to give (in my opinion), what would be a better feel.
How might this be implemented? I assume I could have an onBlur
handler that is triggered.
Is there any way to delay the render() till only after the complete text input?
Danke
Yes if you want to delay a render you just need to return null in the render based on a flag. Lets say you have a state variable inputBlurred that you pass around as a prop to something else.
render() {
if (!this.props.inputBlurred) return null;
... more here
}
You would use it like so.
class ParentComponent extends Component {
constructor() {
super();
this.state = {inputBlurred: false}
}
handleBlur = (e) => {
if(e.target.value){
this.setState({inputBlurred: true});
}
}
render() {
return(
<div>
<input onBlur={this.handleBlur} />
<ChildComponent inputBlurred={this.state.inputBlurred} />
</div>
)
}
}
You have several posibilities.
Create custom Input wrapper around input component, which does trigger "change" event on "complete" change - e.g. on blur. While you typing, parent component won't receive incremental onChange events.
From parent, pass onBlur handler to child input component and as soon, as onBlur handler is triggered, render input's value wherever you wish.
I just got started in React and am confused about something. Let's say I have a Login controller view (smart component), and then a LoginForm view (dumb component), and within the LoginForm, I created custom components for to handle all my inputs on this page the same.
class TextInput extends React.Component {
render() {
return (
<input type="text"
name={this.props.name}
className="form-control"
placeholder={this.props.placeholder}
ref={this.props.name}
onChange={this.props.onChange}
value={this.props.value} />
);
}
}
class LoginForm extends React.Component {
render() {
return (
<TextInput
name="username"
placeholder="Username"
onChange={this.props.onChange}
value={this.props.username} />
<TextInput
name="address"
placeholder="Address"
onChange={this.props.onChange}
value={this.props.address} />
);
}
}
I'm then not sure how in my top level Login controller view's render function, when I create the
How do you get a different to be called for the different inputs? Because my top level component sets the properties down right? Since the bottom most component expects an onChange property, from the top level when I pass down onChange, aren't all TextInputs going to call the same onChange method instead of two separate methods?
Yes, the same function is called. What changes is the parameter. This is just old school JS. The input will pass the change event to to the onChange function as the first param.
You will need to handle that in one component or another.
onChange(e){
var formKeyVal = {};
formKeyVal[e.target.name] = e.target.value;
this.setState(formKeyVal);
}
Thanks to refs We can set up different referring to each low-level element. No matter, dumb or smart.
Your high-level component contains:
`handleText(){
this.setState({
text: ReactDOM.findDOMNode(this.refs.yourElement).value
})
}`
Your low-level component contains:
<input
type="text"
ref="yourElement"
onChange={this.props.textInput}
/>
This example gives you some clue. Pay attention to details: Selector: refs
but element: ref.
From time to time you need two level refs. In this case it would be
text: ReactDOM.findDOMNode(this.refs.yourComponent.refs.yourElement).value