How to initialize form data using async data - reactjs

experts!
I make web service using react.
I want to make page to modify user info.
I can receive user data and set data to input's value.
But, react occur warning it.
Warning: A component is changing an uncontrolled input of type text to be controlled. ~~
I think. I did something wrong. when componentDidMount().
I want to know, how to initialize form data using async data.
Sorry about my poor english skill.
This code is part of my code.
export class UpdatedUser {
#observable private _name: string;
#observable private _phone: string;
#observable private _email: string;
// skip setters, getters.
}
interface MyPageComponentProps extends RouteComponentProps<{}> {
global?: GlobalService;
}
interface MyPageComponentState {
user: UpdatedUser;
}
#inject('global')
#observer
class MyPageComponent extends React.Component<MyPageComponentProps, MyPageComponentState> {
constructor(props: MyPageComponentProps) {
super(props);
this.state = {
user: new UpdatedUser(),
};
}
componentDidMount() {
if (this.props.global) {
userService.getUser(this.props.global.loginedInfo.idx + '').subscribe((res: any) => {
if (res.result === 'success') {
this.setState((prev: MyPageComponentState) => ({
user: update(prev.user, {$set: {
name: res.memberInfo.name,
phone: res.memberInfo.phone,
email: res.memberInfo.email,
} as UpdatedUser})
}));
} else {
alert(res.msg);
}
});
}
}
render() {
return (
<form className="user-info" onSubmit={this.updateUser}>
<h3 className="title">Modify User Info</h3>
<div className="form-group">
<label htmlFor="name" className="form-label">name</label>
<input type="text" id="name" className="form-control" value={this.state.user.name} onChange={this.onChangeInfo} />
</div>
<div className="form-group">
<label htmlFor="phone" className="form-label">phone</label>
<input type="text" id="phone" className="form-control" value={this.state.user.phone} onChange={this.onChangeInfo} />
</div>
<div className="form-group">
<label htmlFor="email" className="form-label">email</label>
<input type="text" id="email" className="form-control" value={this.state.user.email} onChange={this.onChangeInfo} />
</div>
<div className="btn-group">
<button type="submit" className="btn raised primary">수정</button>
<button className="btn raised secondary" onClick={() => this.props.history.goBack()}>취소</button>
</div>
</form>
);
}
export default MyPageComponent;
<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>

The warning you get is about controlled and uncontrolled components. Basically, you can work with elements such as input, textarea and select having the state in the internal state of a React component or keeping it in the DOM.
In your code, you are keeping the UpdatedUser info in the local state and propagating its data with value and onChange, so you are controlling the inputs. However, where is your onChange callback? It does not exist. Try adding that method to your class and it should work. Take a look at the documentation about controlled components
The alternative approach is having uncontrolled inputs, in which case, you need to pass a defaultValue prop to your inputs instead of value and onChange, like this:
<input
defaultValue="Bob"
type="text"
ref={(input) => this.input = input}
/>
If the updateUser method gets triggered, you can simply get the new values from your ref elements:
updateUser(event) {
event.preventDefault()
console.log('New value: ' + this.input.value)
this.props.onSubmit(this.input.value)
}

Related

Keep getting max update exceeded error but cannot seem to find error in code

I have made forms like this before but I seem to be missing something in this one. I keep getting the error "maximum update depth exceeded error" but I dont see where I am goin wrong and I've spent too much time looking at it. I already tried to change my onChange to include an arrow because others have suggested to do so , but when that happens I cant type in the input boxes. like so
onChange={()=>this.handleChange("username")}
I should note that I only get the error when I try to register the user and not when I type into the input. Here is the full error as well.
at checkForNestedUpdates (react-dom.development.js:23804)
at scheduleUpdateOnFiber (react-dom.development.js:21836)
at Object.enqueueSetState (react-dom.development.js:12468)
at Router.Component.setState (react.development.js:366)
at react-router.js:75
at listener (history.js:156)
at history.js:174
at Array.forEach (<anonymous>)
at Object.notifyListeners (history.js:173)
at setState (history.js:562)
Here is my code, please help.
import React from "React"
class Splash extends React.Component{
constructor(props) {
super(props)
this.state = this.props.user;
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.clearErrors();
}
handleSubmit(event) {
event.preventDefault();
this.props.signUp(this.state);
}
handleChange(field) {
return (e) => {
this.setState({ [field]: e.currentTarget.value })
};
}
render() {
return (
<div className="splash-background">
<div className="modal-screeen">
<form className="modal" onSubmit={this.handleSubmit}>
<h2 className="welcom-text"></h2>
<input className="user-input" type="text" placeholder="Name" onChange={this.handleChange("name")} value={this.state.name}/>
<input className="user-input" type="text" placeholder="Email" onChange={this.handleChange("email")} value={this.state.email}/>
<input className="user-input" type="text" placeholder="Create Username" onChange={this.handleChange("username")} value={this.state.username}/>
<input className="user-input" type="password" placeholder="Create Password" onChange={this.handleChange("password")} value={this.state.password}/>
<button>Sign Up</button>
</form>
</div>
</div>
);
}
}
export default Splash
import { connect } from "react-redux";
import { signup, login, clearErrors } from "../../actions/session_actions.js";
import Splash from "./splash";
const mapStateToProps = ({ errors }) => {
return {
errors: errors.session,
user: {
username: "",
password: "",
name:"",
email: "",
},
};
};
const mapDispatchToProps = (dispatch) => {
return {
signUp: (user) => dispatch(signup(user)),
login: (user) => dispatch(login(user)),
clearErrors: () => dispatch(clearErrors()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Splash);
I believe the problem here is the implementation of redux and react state. If you're using redux to manage the form state then I don't think there is a need to also manage that same state with react.
Try something like this, but keep in mind this code isn't tested.
class Splash extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.clearErrors();
}
handleSubmit(event) {
event.preventDefault();
this.props.signUp(this.props.user);
}
handleChange(e) {
// here you would have another action to update redux state depending
// on which input has changed. You can grab the input name via e.target.name
}
render() {
return (
<div className="splash-background">
<div className="modal-screeen">
<form className="modal" onSubmit={this.handleSubmit}>
<h2 className="welcom-text"></h2>
<input
className="user-input"
type="text"
placeholder="Name"
name="name"
onChange={this.handleChange}
value={this.props.user.name}
/>
<input
className="user-input"
type="text"
placeholder="Email"
name="email"
onChange={this.handleChange}
value={this.props.user.email}
/>
<input
className="user-input"
type="text"
placeholder="Create Username"
name="username"
onChange={this.handleChange}
value={this.props.user.username}
/>
<input
className="user-input"
type="password"
placeholder="Create Password"
name="password"
onChange={this.handleChange}
value={this.props.user.password}
/>
<button>Sign Up</button>
</form>
</div>
</div>
);
}
}
export default Splash;
When it comes to form data, I find it's easier to manage just with react state. Generally redux is used to manage state that is shared across the whole application/multiple components.
The problem was actually in my route util file. I had an infinite loop of rerouting!

ReactJS - Warning: A component is changing an uncontrolled input of type text to be controlled - edit function

Hi I'm newbie of react and now I'm starting to learn the basic full-stack concept.
I want to make when User clicked the 'edit' button, the data's in the input text still remained in the text box before user click edit.
but I faced this error
Warning: A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
how can I solve this problem?
thank you in advance!
import React, { Component } from "react";
import axios from "axios";
class Edit extends Component {
constructor(props){
super (props);
this.onChangeName = this.onChangeName.bind(this);
this.onChangePosition = this.onChangePosition.bind(this);
this.onChangePhone = this.onChangePhone.bind(this);
this.onChangePasscode = this.onChangePasscode.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
name:"",
position: "",
phone:"",
passcode: ""
}
}
componentDidMount() {
axios.get('http://localhost:8888/reactJsCRUD/getById.php?id='+this.props.match.params.id)
.then(response => {
this.setState({
name:response.data.employeeName,
postition:response.data.employeePosition,
phone: response.data.employeePhone,
passcode:response.data.passcode
});
})
.catch(function(error) {
console.log(error);
})
}
onChangeName(e) {
this.setState({
name: e.target.value
});
}
onChangePosition(e) {
this.setState({
position: e.target.value
});
}
onChangePhone(e) {
this.setState({
phone:e.target.value
});
}
onChangePasscode(e) {
this.setState({
passcode: e.target.value
});
}
onSubmit(e) {
}
render() {
return (
<div style={{ marginTop: 10 }} className="w-50 p-3">
<h3> Add New Employee</h3>
<form onSubmit={this.onSubmit}>
<div className="form-group-3">
<label>Name: </label>
<input type="text" className="form-control" value={this.state.name} onChange={this.onChangeName} />
</div>
<div className="form-group">
<label htmlFor= "positionFormSelect">Position: </label>
<select className="form-control" id="positionFormSelect" value={this.state.position} onChange={this.onChangePosition}>
<option>Select the position</option>
<option value= "manager">Manager</option>
<option value= "server">Server</option>
<option value= "cook">Cook</option>
</select>
</div>
<div className="form-group">
<label>Phone: </label>
<input type="text" className="form-control" value={this.state.phone} onChange = {this.onChangePhone}/>
</div>
<div className="form-group">
<label>Passcode: </label>
<input type="text" className="form-control" value= {this.state.passcode}onChange = {this.onChangePasscode}/>
</div>
<div className="form-group">
<input
type="submit"
value="Edit Employee"
className="btn btn-primary"
/>
</div>
</form>
</div>
);
}
}
export default Edit;
I think you have a typo here:
<input type="text" className="form-control" value={this.state.posscode} onChange={this.onChangePasscode}/>
value should be this.state.passcode
In your code you are setting the state in componentDidMount and that is why it is showing you a warning.
Because you are using that state for controlled component and if that state get updated from outside of event handler then it will always shows warning as a alert that your state that seems to be used for controlled component, now getting updated outside the scope of controlled component. So advice is that if you want to use state for controlled component, then make sure that specific state do not gets updated from anywhere else.
That's why it is giving you a warning of uncontrolled component because your state is not controlled now as it is getting updated outside of controlled component scope.
Hey guys I got the solution.
The problem was in the PHP file!
<?php
require_once "cors.php";
require_once "connect.php";
cors();
$con = connect();
$id = $_GET['id'];
// Get by id
$sql = "SELECT * FROM employee WHERE employeeId = {$id} LIMIT 1";
$result = mysqli_query($con,$sql);
$row = mysqli_fetch_assoc($result);
print_r($row)
echo $json = json_encode($row);
exit;
because of print_r($row), the got data also had array and that's why I could not access response.data properly in React :) thank you all !

Adding a form to Gatsby JS, with an existing template that is export default

I am attempting to follow this tutorial to add a form to Gatsby JS. I understand it if my file wasn't setup differently. Firstly the tutorials component starts like this
export default class IndexPage extends React.Component {
Where I have this
export default ({ data }) => (
Then I am asked to place the following inside of it. I tried with both the render and return portion, and without.
state = {
firstName: "",
lastName: "",
}
handleInputChange = event => {
const target = event.target
const value = target.value
const name = target.name
this.setState({
[name]: value,
})
}
handleSubmit = event => {
event.preventDefault()
alert(`Welcome ${this.state.firstName} ${this.state.lastName}!`)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
First name
<input
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.handleInputChange}
/>
</label>
<label>
Last name
<input
type="text"
name="lastName"
value={this.state.lastName}
onChange={this.handleInputChange}
/>
</label>
<button type="submit">Submit</button>
</form>
)
}
Here is all my code without the render and return portion
import React from 'react'
import { HelmetDatoCms } from 'gatsby-source-datocms'
import { graphql } from 'gatsby'
import Layout from "../components/layout"
export default ({ data }) => (
<Layout>
state = {
firstName: "",
lastName: "",
}
handleInputChange = event => {
const target = event.target
const value = target.value
const name = target.name
this.setState({
[name]: value,
})
}
handleSubmit = event => {
event.preventDefault()
alert(`Welcome ${this.state.firstName} ${this.state.lastName}!`)
}
<form onSubmit={this.handleSubmit}>
<label>
First name
<input
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.handleInputChange}
/>
</label>
<label>
Last name
<input
type="text"
name="lastName"
value={this.state.lastName}
onChange={this.handleInputChange}
/>
</label>
<button type="submit">Submit</button>
</form>
<article className="sheet">
<HelmetDatoCms seo={data.datoCmsPricing.seoMetaTags} />
<section className="left-package-details">
<h1 className="sheet__title">{data.datoCmsPricing.title}</h1>
<p>
<span>${data.datoCmsPricing.priceAmount}</span> | <span>{data.datoCmsPricing.lengthOfSession}</span>
</p>
{data.datoCmsPricing.details.map(detailEntry => { return <li key={detailEntry.id}> {detailEntry.task}</li>})}
<p>
{data.datoCmsPricing.numberOfSessions}
</p>
book
<p>{data.datoCmsPricing.minimumMessage}</p>
</section>
<section className="right-package-details">
<img src={data.datoCmsPricing.coverImage.url} />
<div
className=""
dangerouslySetInnerHTML={{
__html: data.datoCmsPricing.descriptionNode.childMarkdownRemark.html,
}}
/>
</section>
</article>
</Layout>
)
export const query = graphql`
query WorkQuery($slug: String!) {
datoCmsPricing(slug: { eq: $slug }) {
seoMetaTags {
...GatsbyDatoCmsSeoMetaTags
}
title
priceAmount
details{
task
}
lengthOfSession
numberOfSessions
minimumMessage
descriptionNode {
childMarkdownRemark {
html
}
}
coverImage {
url
}
}
}
`
and the error I get is
There was a problem parsing "/mnt/c/Users/Anders/sites/jlfit-cms/src/templates/pricingDetails.js"; any GraphQL
fragments or queries in this file were not processed.
This may indicate a syntax error in the code, or it may be a file type
that Gatsby does not know how to parse.
File: /mnt/c/Users/Anders/sites/jlfit-cms/src/templates/pricingDetails.js
The problem you are facing is because you are trying to use state (and setState) on a functional component when the example uses a class.
Functional components don't have the same tools/syntax/APIs available to you as a class component (for better or worse) so you have to ensure you're using the correct approach for each case.
In the most recent versions of React you can have the equivalent of state and setState made available to you by using React hooks, more specifically the useState hook.
I've put together a quick working example of the code you pasted in your question converted to React hooks. You can find it on this sandbox.
I recommend you have a read over the initial parts of the React docs to ensure you're familiar with the foundational concepts or React, it will save a lot of headache in the future. 🙂

Setting the default value of an input field after data is retrieved causes the content to overlap and the "onChange" event not to be triggered

I have an "edit category" component in my React application.
The ID is passed through the URL.
When the component is mounted, the action "fetchCategory" is called, which updates the props on the component with the current category.
I have a form which I want to be pre-populated, which I'm currently doing using the defaultValue on the input.
However, this isn't reflected on the state and the label for the text field overlaps the input field.
Any help would be greatly appreciated. I'll leave snippets of my code below which could help with understanding what I'm trying to do.
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchCategory } from "../../store/actions/categoryActions";
class AddOrEditCategory extends Component {
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id);
if (this.props.match.params.id) {
this.setState({
_id: this.props.match.params.id
});
}
}
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
render() {
const addingNew = this.props.match.params.id === undefined;
return (
<div className="container">
<h4>{addingNew ? "Add category" : "Edit category"}</h4>
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
defaultValue={this.props.category.name}
onChange={this.handleChange}
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
defaultValue={this.props.category.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
category: state.categoryReducer.category
};
};
export default connect(
mapStateToProps,
{ fetchCategory }
)(AddOrEditCategory);
EDIT: Included whole component as requested
You need to replace the 'defaultValue' attribute with 'value' in the inputs.
You are using a controlled vs uncontrolled component. You dont need to use defaultValue.
You can set the initial values on the promise success for fetchCategory
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id).then(response => {
// Set the initial state here
}
}
OR in
componentWillReceiveProps(nextProps) {
// Compare current props with next props to see if there is a change
// in category data received from action fetchCategory and set the initial state
}
React docs
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
onChange={this.handleChange}
value={this.state.name} //<---
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
value={this.state.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>

Correct way to take input values in React.js

I am new to react and i am wondering if this is the correct way (somewhat quallity code) to get values from input forms.
import React from 'react';
class Form extends React.Component {
constructor() {
super();
this.state = {
nameValue: '',
ageValue: ''
}
}
onChangeName(event) {
this.setState({
nameValue:event.target.value
});
}
onChangeAge(event) {
this.setState({
ageValue: event.target.value
});
}
showValue(){
alert('name '+ this.state.nameValue + ' age: ' + this.state.ageValue)
}
render() {
return (
<form>
<label>Name:
<input type="text" onChange={this.onChangeName.bind(this)}/>
</label>
<label>Age:
<input type="text" onChange={this.onChangeAge.bind(this)}/>
</label>
<input type="submit" value="Submit" onClick={this.showValue.bind(this)}/>
</form>
);
}
}
export default Form;
I mean i heared that the way it's done in react is so that every component should be somewhat independant and have it's own behaviour. Btw the code works just asking for the qualiity cause i have project in from of me. Or should i just add an event to the button and make the other componenents stateless, i.e the 2 input fields
Yes, this is the correct way.
Suggestions:
1. Instead of using two different change function, you can handle all the input elements change by a single change function. For that assign the name (same as state variable name) property with input element, and inside change function access that state variable name by event.target.name and update that.
Run this snippet:
class Form extends React.Component {
constructor() {
super();
this.state = {
nameValue: '',
ageValue: ''
}
this.commonChange = this.commonChange.bind(this);
this.showValue = this.showValue.bind(this);
}
commonChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
showValue(){
event.preventDefault();
alert('name '+ this.state.nameValue + ' age: ' + this.state.ageValue)
}
render() {
return (
<form>
<label>Name:
<input type="text" name="nameValue" onChange={this.commonChange}/>
</label>
<label>Age:
<input type="text" name="ageValue" onChange={this.commonChange}/>
</label>
<input type="submit" value="Submit" onClick={this.showValue}/>
</form>
);
}
}
ReactDOM.render(<Form/>, document.getElementById('app'))
<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='app'/>
2. You are not controlling the input elements by state values (not using the value property of input elements), just storing the values in state, to get those value during submit call. So storing the values in state variable is not required, you can use uncontrolled component, and use ref to get the values.
Define the ref like this:
<input type="text" ref={el => this.nameValue} />
And access the value by this.nameValue.value
Run this snippet:
class Form extends React.Component {
constructor() {
super();
this.showValue = this.showValue.bind(this);
}
showValue(){
alert('name '+ this.nameValue.value + ' age: ' + this.ageValue.value)
}
render() {
return (
<form>
<label>Name:
<input type="text" ref={el => this.nameValue=el} />
</label>
<label>Age:
<input type="text" ref={el => this.ageValue=el} />
</label>
<input type="submit" value="Submit" onClick={this.showValue}/>
</form>
);
}
}
ReactDOM.render(<Form/>, document.getElementById('app'))
<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='app'/>
3. Always define binding of the functions in the constructor.
Check this answer for more details: why do you need to bind a function in a constructor

Resources