React. How to populate the form after the login is completed - reactjs

I have two react components(SignupDetails.js, BasicInformation.js). SignupDetails is obviously responsible to signup the user, it will get the user information from the form and submit it to the server through axios.
On the server side(backend), if the user already exist, the server will then collect the remain information, such as first name, middle name, etc and then send it back to be collected on the client side.
Back to the client side, now the remain user information which has been returned is then stored in the central store using the redux-reducers and the page is then "redirected" to BasicInformation.js
Now on the BasicInformation.js, mapStateToPros is then executed so I will do have access to the information that has been stored on the central store but then it becomes the problem: I am struggling to show the remain information. This sounds pretty simple but I tried many things on the componentDidUpdate and on the render method but without being successful.
Please find the code below and let me know if you any ideas.
import React, {Component} from 'react';
import classes from './SignUp.module.css';
import {connect} from 'react-redux'
import * as actions from '../../store/actions/index';
import Spinner from '../../components/UI/Spinner/Spinner'
class SignupDetails extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: ''
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
postDataHandler = () => {
const data = {
username: this.state.email,
email: this.state.email,
password: this.state.password
};
this.props.onSignupDetails(data);
}
componentDidMount() {
this.props.history.push({pathname: '/signup_details/'})
}
componentDidUpdate() {
if (this.props.signup_details_success)
this.props.history.push({pathname: '/basic_information/'})
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.signup_details_response}</p>
);
}
return (
<div>
<Spinner show={this.props.loading}/>
<div className={classes.SignUpForm}>
{errorMessage}
<h3>Take your first step.</h3>
<div>
<input
key="email"
name="email"
value={this.state.email}
onChange={this.handleInputChange}/>
</div>
<div>
<input
key="password"
name="password"
value={this.state.password}
onChange={this.handleInputChange}/>
</div>
<button className={classes.OkButton} onClick={this.postDataHandler}>Next</button>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
signup_details: state.signup.signup_details,
signup_details_success: state.signup.signup_details_success,
signup_details_response: state.signup.signup_details_response,
loading: state.signup.loading,
error: state.signup.error
};
};
const mapDispatchToProps = dispatch => {
return {
onSignupDetails: (signup_details) => dispatch(actions.signupDetails(signup_details))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SignupDetails);
-----
import React, {Component} from 'react';
import classes from './SignUp.module.css';
import {connect} from 'react-redux'
import * as actions from '../../store/actions/index';
import Spinner from '../../components/UI/Spinner/Spinner'
class BasicInformation extends Component {
constructor(props) {
super(props)
this.state = {
first_name: '',
middle_name: '',
last_name: '',
mobile_number: '',
fund_balance: ''
}
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
postDataHandler = () => {
const data = {
username: this.state.email,
email: this.state.email,
first_name: this.state.first_name,
middle_name: this.state.middle_name,
last_name: this.state.last_name,
mobile_number: this.state.mobile_number,
sfunds: [{balance: this.state.fund_balance}]
};
this.props.onSignupBasicInformation(data);
}
componentDidUpdate() {
if (this.props.signup_basic_information_success)
this.props.history.push({pathname: '/personal_information/'})
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.signup_basic_information_response}</p>
);
}
return (
<div>
<Spinner show={this.props.loading}/>
<div className={classes.SignUpForm}>
{errorMessage}
<h3>First we need some basic information</h3>
<div><input
key="first_name"
name="first_name"
value={this.state.first_name}
onChange={this.handleInputChange}/></div>
<div><input
key="middle_name"
name="middle_name"
value={this.state.middle_name}
onChange={this.handleInputChange}/></div>
<div><input
key="last_name"
name="last_name"
value={this.state.last_name}
onChange={this.handleInputChange}/></div>
<div><input
key="mobile_number"
name="mobile_number"
value={this.state.mobile_number}
onChange={this.handleInputChange}/></div>
<div><input
key="fund_balance"
name="fund_balance"
value={this.state.fund_balance}
onChange={this.handleInputChange}/></div>
<button className={classes.OkButton} onClick={this.postDataHandler}>Next</button>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
signup_details_success: state.signup.signup_details_success,
signup_details_response: state.signup.signup_details_response,
signup_basic_information: state.signup.signup_basic_information,
signup_basic_information_success: state.signup.signup_basic_information_success,
signup_basic_information_response: state.signup.signup_basic_information_response,
loading: state.signup.loading,
error: state.signup.error
};
};
const mapDispatchToProps = dispatch => {
return {
onSignupBasicInformation: (basic_information) => dispatch(actions.signupBasicInformation(basic_information))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(BasicInformation);

In case someone wonder, I managed to solve this problem through componentDidMount to set the local state.
componentDidMount(){
if(this.props.smsf_member_details) {
this.setState({
first_name: this.props.smsf_member_details.first_name,
last_name: this.props.smsf_member_details.last_name,
middle_name: this.props.smsf_member_details.middle_name,
});
}
}

Related

Accessing state from another component

I am attempting to access state from another component in the correct way. From what I understand is you cannot access state directly but the state should be lifted. For my purpose I am trying to access the name of the user from my profile component in my form component with localStorage.
My Profile Component ....
import React, { Component } from 'react';
import NavigationBar from './NavigationBar'
export default class Profile extends Component {
documentData;
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.state = {
name: '',
email: '',
phone: '',
address1: '',
address2: '',
addresscitystate: '',
addresszip: ''
}
}
handleChange= (e)=> {
this.setState({[e.target.name]:e.target.value});
}
// on form submit...
handleFormSubmit(e) {
e.preventDefault()
localStorage.setItem('document',JSON.stringify(this.state));
alert ('Profile has been updated')
}
// React Life Cycle
componentDidMount() {
this.documentData = JSON.parse(localStorage.getItem('document'));
if (localStorage.getItem('document')) {
this.setState({
name: this.documentData.name,
phone: this.documentData.phone,
email: this.documentData.email,
address1: this.documentData.address1,
address2: this.documentData.address2,
addresscitystate: this.documentData.addresscitystate,
addresszip: this.documentData.addresszip,
})
} else {
this.setState({
name: '',
phone: '',
email: '',
address1: '',
address2: '',
addresscitystate: '',
addresszip: ''
})
}
}
render() {
return (
<div className="container">
<NavigationBar />
<div>
<h1 className='profile-title'> Profile </h1>
</div>
<form onSubmit={this.handleFormSubmit}>
<div className="form-group">
**<input type="text" name="name" className="form-control" value={this.state.name} onChange={this.handleChange} />
<label>Name</label>**
</div>
....
My Form Component....
import {useState, useEffect} from 'react'
const Form = () => {
//initial state
const [transaction, setTransaction] = useState({
description: '',
amount: ''
})
const [list, setList] = useState(
JSON.parse(localStorage.getItem('list')) || []
)
const [balance, setBalance] = useState('')
const [income, setIncome] = useState(
JSON.parse(localStorage.getItem('income'))
)
const [expense, setExpense] = useState(JSON.parse(localStorage.getItem('expense')))
//updates based onChange value
const updateBalance = (e) => {
setTransaction({
...transaction,
[e.target.name]:
e.target.type === 'number' ? parseInt(e.target.value) : e.target.value
})
}
//identify if transaction is income/expense
const plusMinus = () => {
transaction.amount > 0
? setIncome(income + transaction.amount)
: setExpense(expense + transaction.amount)
}
// updates balance after transaction is added
const getBalance = () => {
const amounts = list.map(i => i.amount);
const money = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);
setBalance(money)
}
useEffect(() => {
getBalance()
localStorage.setItem('list', JSON.stringify(list))
localStorage.setItem('income', JSON.stringify(income))
localStorage.setItem('expense', JSON.stringify(expense))
}, [])
//clear transaction list
const clearBudget = () => {
localStorage.clear();
alert ('Balance has been cleared, Please Refresh Page')
}
const onSubmit = e => {
e.preventDefault();
setList([transaction, ...list])
plusMinus()
setTransaction({ description: '', amount: ''})
}
return (
**<div>
<div className='totals'>
<h2 className='balance'> Hello User, Your Current Balance </h2>
<h3> ${balance} </h3>
</div>**
...
Any advise on how to start would be appreciated!
Thanks.
Since you want to make your user accessible within your whole app passing it down to your component is not advisable as it'll cause prop drilling which is not a good practise. To avoid that problem then you should consider using a built-in feature of react called ContextAPI or any other state management library like Redux of Flux. But since your app is not complex then, ContextApi would be a good choice.
When you need to access state from multiple components you should either use Context API or a State Management Library such as Redux. Please note that Redux is used mostly in bigger applications, the ContextAPI should be more than enough for your use case.

How to make an axios POST request in React?

So, some context: Users submit a dog name via a text input, and this is controlled by the 'Dogue.jsx' component:
import React from 'react';
class Dogue extends React.Component {
constructor(props) {
super(props);
this.state = {
id: props.id,
nameInput: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
nameInput: e.target.value,
});
}
handleSubmit(e) {
e.preventDefault();
this.props.inputFunction(this.state.nameInput);
}
render() {
console.log(this.props.id);
return (
<div className="dogue-container">
<img className="img" src={this.props.dogList} />
<br />
<form onSubmit={this.handleSubmit} className="form">
<input
onChange={this.handleChange}
className="input"
type="text"
placeholder="Enter dog name"
/>
<br />
<button className="button">Submit</button>
</form>
<h2 className="text">Name: {this.props.name} </h2>
</div>
);
}
}
export default Dogue;
The submitted information is then passed to 'App.jsx', where it is used to update state:
import React, {Component} from 'react';
import './styles.css';
import DogList from './DogList';
import axios from 'axios';
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
dog: [],
dogName: [],
};
this.updateStateWithInput = this.updateStateWithInput.bind(this);
}
setData = async () => {
const x = await fetch('https://dog.ceo/api/breed/hound/images');
const y = await x.json();
const z = await y.message;
let newArr = [];
for (let i = 0; i < z.length; i++) {
if (i <= 9) {
newArr.push(z[i]);
}
}
return newArr;
};
async componentDidMount() {
this.setState({
loading: true,
});
let dogPromise = await this.setData();
let dogNamePromise = await axios.get('http://localhost:3000/dogs');
this.setState({
loading: false,
dog: dogPromise,
dogName: dogNamePromise.data,
});
}
updateStateWithInput(nameInput) {
//Here is where state is updated.
//change state, then use axios.post to submit data
}
render() {
return this.state.loading ? (
<h1 className="text"> Dogues Loading.....</h1>
) : (
<div>
<h1 className="text">Rate My Dogue</h1>
<DogList
dogs={this.state.dog}
name={this.state.dogName}
inputFunction={this.updateStateWithInput}
/>
</div>
);
}
}
export default App;
The updated state, I imagine, will be used in the axios post request to submit data to the database. So, I've got input data being sent from Dogue to App, I'm just not sure what to do now? The information currently in state looks as follows:
[
{
id: 1,
dogName: 'bruce',
},
{
id: 2,
dogName: 'borker',
},
{
id: 3,
dogName: 'henry',
},
];
I should also show my map function, in DogList.jsx:
import React from 'react';
import Dogue from './Dogue';
const DogList = (props) => {
return (
<div className="img-container">
{props.dogs.map((doggie, index) => {
return (
<Dogue
id={props.name[index] && props.name[index].id}
key={index}
dogList={doggie}
name={props.name[index] && props.name[index].dogName}
inputFunction={props.inputFunction}
/>
);
})}
</div>
);
};
export default DogList;
You can send a POST request with axios by calling:
axios.post(url, data, options);
It’s similar to the way you called the get method to make a GET request.
I’m leaving this axios cheat sheet here since it’s really useful until you get the hang of it:
https://kapeli.com/cheat_sheets/Axios.docset/Contents/Resources/Documents/index

Set State using query component React apollo

I have used same form to create and update account(module). Create is working fine but in update mode I am not able to set form field value using Set State methods. I have used query component on render methods and setstate not working on rendor method.
import { Mutation } from "react-apollo";
import { Query } from "react-apollo";
import gql from "graphql-tag";
import React, { Component } from "react";
import Router from "next/router";
import Joi from "joi-browser";
const CREATE_ACCOUNT = gql`
mutation CreateAccount(
$name: String
$phone_office: String
$website: String
) {
createAccount(name: $name, phone_office: $phone_office, website:
$website) {
name
phone_office
website
}
}
`;
export const allAccountbyidQuery = gql`
query account($id: String) {
account(id: $id) {
id
name
phone_office
website
}
};
const schema = {
name: Joi.string()
.required()
.error(errors => {
return {
message: "Name is required!"
};
}),
phone_office: Joi.string()
.required()
.error(errors => {
return {
message: "Phone Number is required!"
};
}),
website: Joi.string()
.required()
.error(errors => {
return {
message: "Website is required!"
};
})
};
Main class component
class CreateAccountModule extends React.Component {
static async getInitialProps({ query }) {
const { id } = query;
return { id };
}
constructor(props) {
super();
this.state = {
isFirstRender: true,
name: "",
phone_office: "",
website: ""
};
}
handleChange = event => {
console.log("hello");
const { name, value } = event.target;
this.setState({ [name]: value });
};
validate(name, phone_office, website) {
let errors = "";
const result = Joi.validate(
{
name: name,
phone_office: phone_office,
website: website
},
schema
);
if (result.error) {
errors = result.error.details[0].message;
}
return errors;
}
setName = name => {
if (this.state.isFirstRender) {
this.setState({ name, isFirstRender: false });
}
};
render() {
let input;
const { errors } = this.state;
console.log(this.props);
const allAccountbyidQueryVars = {
id: this.props.id
};
//console.log(allAccountbyidQueryVars);
return (
<Query
query={allAccountbyidQuery}
variables={allAccountbyidQueryVars}
onCompleted={data => this.setName(data.account.name)}
>
{({ data, loading, error }) => {
<CreateAccountModule account={data.account} />;
return (
<Mutation mutation={CREATE_ACCOUNT}>
{(createAccount, { loading, error }) => (
<div>
<form
onSubmit={e => {
e.preventDefault();
const errors = this.validate(
e.target.name.value,
e.target.phone_office.value,
e.target.website.value
);
if (errors) {
this.setState({ errors });
return;
}
if (!errors) {
let accountres = createAccount({
variables: {
name: e.target.name.value,
phone_office: e.target.phone_office.value,
website: e.target.website.value
}
}).then(() => Router.push("/"));
input.value = "";
}
}}
>
{errors && <p>{errors}</p>}
<input
type="text"
name="name"
id="name"
placeholder="Name"
value={this.state.name}
onChange={this.handleChange}
/>
<input
name="phone_office"
id="phone_office"
placeholder="Phone Number"
//value={data.account.phone_office}
//value="123456"
onChange={this.handleChange}
/>
<input
name="website"
id="website"
placeholder="Website"
//value={data.account.website}
onChange={this.handleChange}
/>
<button type="submit">Add Account</button>
<button type="button">Cancel</button>
</form>
{loading && <p>Loading...</p>}
{error && <p>Error :( Please try again</p>}
</div>
)}
</Mutation>
);
}}
</Query>
);
}
}
export default CreateAccountModule;
`
I have tried with props but get props data in apollo state. anyone please suggest possible solution to fix this issue.

FormValidation with Button State

In my React app, I have a component call <ComingSoonForm/> Inside that, I have a TextInputField. If the Email valid, the Notify-Button is able. If the Email is invalid, the Notify-Button is disabled.
Here is my Component File:
import React, { Component } from "react";
import { TextInputField, toaster, Button, } from "evergreen-ui";
import Box from 'ui-box';
import { validateEmail } from "../FormValidation/FormValidator";
class ComingSoonForm extends Component {
constructor(props) {
super(props);
this.state = {
emailErr: {
status: true,
value: ""
},
email: "",
isDisabled: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
this.checkFormStatus = this.checkFormStatus.bind(this);
}
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email});
console.log(this.state.email);
this.checkFormStatus();
};
handleSubmit() {
if (this.checkFormStatus()) {
alert("Form is OK")
}
}
checkFormStatus() {
// form validation middleware
const { email } = this.state;
const emailErr = validateEmail(email);
if (!emailErr.status) {
this.setState({isDisabled:false})
return true;
} else {
this.setState({
emailErr,
});
return false;
}
}
render() {
return (
<div>
<Box className="welcomePageWelcomeInnerLoginButton">
<TextInputField
marginTop={15}
width={200}
onChange={this.handleEmailInput}
value={this.state.email}
type="email"
placeholder="Your email-address"
inputHeight={40}
/>
</Box>
<Button height="40" appearance="primary" marginBottom={5} className="welcomePageWelcomeInnerLoginButtonWidth" disabled={this.state.isDisabled} onClick={this.handleSubmit}>Notify Me</Button>
</div>
);
}
}
export default ComingSoonForm;
But this case doesn't work correctly. So when the command console.log(this.state.email) in the handleEmailInput Function run, I get the following data in the console:
I type one letter (t) and I get:
//ComingSoonForm.js:25
I type a second letter (t) and I get:
t //ComingSoonForm.js:25
t //FormValidator.js:10
Why do I have to enter two letters in order for one to appear in the console?
setState is asynchronous, you can pass a callback method as a second parameter like this:
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email }, () => console.log(this.state.email));
this.checkFormStatus();
};

Property not found in props of React element `form`

Hi i'm having problems with Flow and React.
I'm getting these errors and I want to get rid of them. What am I missing I have search everywhere. I understand that the props are missing but I can't seem to find where to define them.
Flow: property className. Property not found in props of React element form
Flow: property onSubmit. Property not found in props of React element form
export default class LoginForm extends React.Component {
_submitForm: Function;
constructor(props?: {}) {
super(props);
this.state = {
form: {
email: '',
password: '',
},
errors: {
_form: "",
email: "",
password: ""
}
};
this._submitForm = this._submitForm.bind(this);
}
_handleValues(param: string, value?: string) {
let obj = this.state;
obj['form'][param] = value;
this.setState(obj);
}
_submitForm(event: Event) {
this._clearErrors(event);
let form = this.state.form;
AxiosQueue
.post({
url: LINK.AUTHENTICATE,
data: form
})
.then(({data}) => {
if (!data.success) {
return;
}
})
.catch((response) => {
console.error(response);
});
}
render() {
const {errors, form} = this.state;
const user = UserStore.getUser();
const formText = FORM_TEXT[user.language || "en_AU"];
return (
<form className="form-inline" onSubmit={this._submitForm}>
{errors._form}
<InputEmail id="email" error={errors.email} value={form.email} callback={this._handleValues}/>
<InputPassword id="password" error={errors.password} value={form.password}
callback={this._handleValues}/>
<button type="submit" className="btn btn-default">{formText.LOGIN}</button>
</form>
);
}
}
There is conflict with your variable name form in Syntex const {errors, form} = this.state; and form component. Solution is to give some other name in this.state. Like
this.state = {
formValidation:{
//Validation properties
}
}
And consume so that will remove conflict
const {formValidation} = this.state

Resources