React and useReducer unable to access the state value - reactjs

relatively new to React so please excuse me for this question.
Below is a VERY basic form with a submission button and ability to listen for value changes on a users username and password.
The problem that I am facing is that as soon as I type anything into the form, it throws this error:
[![enter image description here][1]][1]
(image isn't uploading: TypeError: Cannot read property "requestInFlight" of undefined)
Apparently state is nowhere to be found. I feel that I have done this verbatim from tutorials and the [documentations][2] so not sure what is throwing me off here.
import React, { useContext, useEffect, useReducer } from "react";
import DispatchContext from "../DispatchContext";
// Boostrap Components
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
// Third party Components
import Axios from "axios";
const ACTIONS = {
UPDATE_USERNAME: "update-username",
UPDATE_PASSWORD: "update-password",
REQUEST: "request",
};
const initialState = {
username: "",
password: "",
requestInFlight: false,
};
function reducer(draft, { type, value }) {
switch (type) {
case ACTIONS.UPDATE_USERNAME:
draft.username = value;
return;
case ACTIONS.UPDATE_PASSWORD:
draft.password = value;
return;
case ACTIONS.REQUEST:
draft.requestInFlight = value;
return;
default:
return;
}
}
function FormComponent({ type }) {
const appDispatch = useContext(DispatchContext);
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
if (!state.requestInFlight) return;
dispatch({ type: ACTIONS.REQUEST, value: true });
const ourRequest = Axios.CancelToken.source();
async function sendRequest() {
try {
const response = await Axios.post(`/api/${type}`, {
username: state.username,
password: state.password,
});
console.log(response);
dispatch({ type: ACTIONS.REQUEST, value: false });
} catch (e) {
console.log(e);
dispatch({ type: ACTIONS.REQUEST, value: false });
}
}
sendRequest();
return () => ourRequest.cancel();
}, [state.requestInFlight]);
return (
<Form
onSubmit={(e) => {
e.preventDefault();
dispatch({ type: ACTIONS.REQUEST, value: true });
}}
>
<Form.Group controlId="formBasicEmail">
<Form.Label className="text-muted">Username</Form.Label>
<Form.Control
type="text"
placeholder="Enter Username"
onChange={(e) =>
dispatch({ type: ACTIONS.UPDATE_USERNAME, value: e.target.value })
}
/>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label className="text-muted">Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={(e) =>
dispatch({ type: ACTIONS.UPDATE_PASSWORD, value: e.target.value })
}
/>
</Form.Group>
<Button variant="primary" type="submit">
{type}
</Button>
</Form>
);
}
export default FormComponent;
[1]: https://i.stack.imgur.com/jD0sO.png
[2]: https://reactjs.org/docs/hooks-reference.html#usereducer

Your reducer setup is wrong, its returning undefined once it runs initially. You need to return your updated object instead of undefined.
function reducer(draft, { type, value }) {
switch (type) {
case ACTIONS.UPDATE_USERNAME:
return {...draft, username: value };
case ACTIONS.UPDATE_PASSWORD:
return {...draft, password: value };
case ACTIONS.REQUEST:
return {...draft, requestInFlight: value };
default:
return draft;
}
}

The first answer is the correct answer but there is something I need to point out from your implementation.
Reducer functions have to be pure!
why? I recommend reading this article: https://www.freecodecamp.org/news/why-redux-needs-reducers-to-be-pure-functions-d438c58ae468/

Related

React-redux useSelector state not working in onSubmit function but working in other functions

Please can someone explain to me why the second implementation of my code is reading the status_code of the msg and my first implementation can't read the status_code of the msg?
First implementation
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import InputField from '../components/layout/InputField';
import { faShop, faGlobe, faHouse, faSign, faMapLocationDot, faCity, faEarthEurope, faUpload } from '#fortawesome/free-solid-svg-icons';
import Button from '../components/layout/Button';
import FormAlert from '../components/layout/FormAlert';
import { createOrUpdateStore, clearMessages } from '../reducers/storeSlice';
const CreateStore = () => {
const [formData, setFormData ] = useState({
name: '',
shop_url: '',
house: '',
street: '',
postalcode: '',
city: '',
country: ''
});
const dispatch = useDispatch();
const { loading, store, msg, errors } = useSelector((state) => state.store);
const navigate = useNavigate();
const {
name,
shop_url,
house,
street,
postalcode,
city,
country,
} = formData;
useEffect(() => {
if (msg) {
dispatch(clearMessages());
}
}, []);
const onChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const getError = (name) => {
const findError = errors.filter(error => error.param === name);
if (findErfind errorh > 0) {
const error = errors.find(error => error.param === name);
return error;
}
}
const onSubmit = (e) => {
e.preventDefault();
const result = dispatch(createOrUpdateStore(formData));
if (result) {
if (msg.status_code === '201') {
navigate('/login');
}
}
}
return (
<form onSubmit={(e) => onSubmit(e)} className='dashboard-form'>
<span className='header-text'>Create your store</span>
{JSON.stringify(msg) !== '{}' ? (<FormAlert alert={msg} />) : ''}
<InputField type='text' label='Store name' name='name' value={name} changeHandler={onChange} error={getError('name')} icon={faShop} />
<InputField type='text' label='Website address' name='shop_url' value={shop_url} changeHandler={onChange} error={getError('shop_url')} icon={faGlobe} />
<InputField type='text' label='House' name='house' value={house} changeHandler={onChange} error={getError('house')} icon={faHouse} />
<InputField type='text' label='Street' name='street' value={street} changeHandler={onChange} error={getError('street')} icon={faSign} />
<InputField type='text' label='Postalcode' name='postalcode' value={postalcode} changeHandler={onChange} error={getError('postalcode')} icon={faMapLocationDot} />
<InputField type='text' label='City' name='city' value={city} changeHandler={onChange} error={getError('city')} icon={faCity} />
<InputField type='text' label='Country' name='country' value={country} changeHandler={onChange} error={getError('country')} icon={faEarthEurope} />
<Button text='CREATE' loading={loading} icon={faUpload} />
</form>
)
}
export default CreateStore;
Second implementation
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import InputField from '../components/layout/InputField';
import { faShop, faGlobe, faHouse, faSign, faMapLocationDot, faCity, faEarthEurope, faUpload } from '#fortawesome/free-solid-svg-icons';
import Button from '../components/layout/Button';
import FormAlert from '../components/layout/FormAlert';
import { createOrUpdateStore, clearMessages } from '../reducers/storeSlice';
const CreateStore = () => {
const [formData, setFormData ] = useState({
name: '',
shop_url: '',
house: '',
street: '',
postalcode: '',
city: '',
country: ''
});
const dispatch = useDispatch();
const { loading, store, msg, errors } = useSelector((state) => state.store);
const navigate = useNavigate();
const {
name,
shop_url,
house,
street,
postalcode,
city,
country,
} = formData;
useEffect(() => {
if (msg) {
dispatch(clearMessages());
}
}, []);
const onChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const getError = (name) => {
const findError = errors.filter(error => error.param === name);
if (findError.length > 0) {
const error = errors.find(error => error.param === name);
return error;
}
}
const onSubmit = (e) => {
e.preventDefault();
dispatch(createOrUpdateStore(formData));
}
const redirectOnSuccess = (msg) => {
if (msg.status_code === '201') {
setTimeout(() => {
dispatch(clearMessages());
}, 2000);
setTimeout(() => {
dispatch(clearMessages());
navigate('/store');
}, 3000);
}
}
redirectOnSuccess(msg);
return (
<form onSubmit={(e) => onSubmit(e)} className='dashboard-form'>
<span className='header-text'>Create your store</span>
{JSON.stringify(msg) !== '{}' ? (<FormAlert alert={msg} />) : ''}
<InputField type='text' label='Store name' name='name' value={name} changeHandler={onChange} error={getError('name')} icon={faShop} />
<InputField type='text' label='Website address' name='shop_url' value={shop_url} changeHandler={onChange} error={getError('shop_url')} icon={faGlobe} />
<InputField type='text' label='House' name='house' value={house} changeHandler={onChange} error={getError('house')} icon={faHouse} />
<InputField type='text' label='Street' name='street' value={street} changeHandler={onChange} error={getError('street')} icon={faSign} />
<InputField type='text' label='Postalcode' name='postalcode' value={postalcode} changeHandler={onChange} error={getError('postalcode')} icon={faMapLocationDot} />
<InputField type='text' label='City' name='city' value={city} changeHandler={onChange} error={getError('city')} icon={faCity} />
<InputField type='text' label='Country' name='country' value={country} changeHandler={onChange} error={getError('country')} icon={faEarthEurope} />
<Button text='CREATE' loading={loading} icon={faUpload} />
</form>
)
}
export default CreateStore;
I tried several methods for getting the msg in the onSubmit function but they didn't work. So I read the documentation of the react-redux toolkit and found out that you can use data gotten from the state with useSelector, and that you can use this data in any function but it still refused to work in the onSubmit() function. And this explains why the errors array was retrieved in the getError() function.
Issues
If I had to hazard a guess it is that the createOrUpdateStore is an asynchronous action that should be awaited, and there's a stale closure over the selected msg state in the onSubmit callback. The second implementation skips both these issues by not waiting for any returned Promises from createOrUpdateStore to resolve and relying on the CreateStore component to rerender when the redux state updates.
Solution to fix implementation 1
Assuming this asynchronous createOrUpdateStore action returns a Promise it can be awaited. To overcome the stale msg closure the submit handler will need to manually access the store to get the current state.
// declare submit handler async
const onSubmit = async (e) => {
e.preventDefault();
// await asynchronous action to resolve with result
const result = await dispatch(createOrUpdateStore(formData));
if (result) {
// have result, get msg from current state
const state = store.getState();
const { msg } = state.store;
if (msg.status_code === '201') {
navigate('/login');
}
}
}
Solution to fix implementation 2
The redirectOnSuccess function is called unconditionally as an unintentional side-effect and issues other unintentional side-effect. To make these intentional side-effects the logic should be moved into a useEffect hook with appropriate dependency.
const {
loading,
store,
msg,
errors
} = useSelector((state) => state.store);
const navigate = useNavigate();
...
useEffect(() => {
if (msg.status_code === '201') {
dispatch(clearMessages());
navigate('/store');
}
}, [msg, navigate]);
You have to use a preventDefault to prevent the default submit action and it’s done by the code below:
Const onClick=(event)=>{
event.preventDefault()
// then you can have your redux function calls
}

React input tag disabled prop issue

I am trying to build a login form in React.js. I want to enable/disable Login button based on results return by validate method. React throws error 'Invalid value for prop disabled on tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.'. Does anyone have came across same error? Help me to understand what is going wrong here?
import React, { Component } from "react";
import Input from "../common/input";
import Joi from "joi-browser";
class LoginForm extends Component {
state = {
account: {
username: "",
password: "",
},
errors: {},
};
schema = {
username: Joi.string().required().label("Username"),
password: Joi.string().required().label("Password"),
};
abortEarly = {
abortEarly: false,
};
handleSubmit = (event) => {
event.preventDefault();
const errors = this.validate();
if (errors) return;
console.log("submitted");
};
validate = () => {
const result = Joi.validate(
this.state.account,
this.schema,
this.abortEarly
);
const errors = {};
if (!result.error) return null;
result.error.details.map((detail) => {
errors[detail.path[0]] = detail.message;
return detail.path[0];
});
// console.log(errors);
this.setState({ errors });
return errors;
};
validateProperty = ({ name, value }) => {
const propertyTobeValidated = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(propertyTobeValidated, schema);
return error ? error.details[0].message : null;
};
handleChange = ({ currentTarget }) => {
const errors = { ...this.state.errors };
const error = this.validateProperty(currentTarget);
if (error) errors[currentTarget.name] = error;
else delete errors[currentTarget.name];
const account = { ...this.state.account };
account[currentTarget.name] = currentTarget.value;
this.setState({ account, errors });
};
render() {
const { account, errors } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
label="Username"
name="username"
value={account.username}
onChange={this.handleChange}
error={errors.username}
></Input>
<Input
label="Password"
name="password"
value={account.password}
onChange={this.handleChange}
error={errors.password}
></Input>
<button disabled={this.validate} className="btn btn-primary">
Login
</button>
</form>
</div>
);
}
}
export default LoginForm;
Disabled is a boolean property, meaning it can only have a value of true or false. Instead of a boolean, your validate function is returning an object, thus React throws an "Invalid value" error. In order to fix this you could check if the result of this.validate is null:
<button
disabled={(this.validate() !== null)}
className="btn btn-primary"
>
Login
</button>
Also, you forgot to call your this.validate at all :)
Regarding the "Maximum update depth...", you should remove the this.setState from the this.validate, because you are already putting the error in state in the handleChange method.
Here you go with a solution
import React, { Component } from "react";
import Input from "../common/input";
import Joi from "joi-browser";
class LoginForm extends Component {
state = {
account: {
username: "",
password: "",
},
errors: {},
};
schema = {
username: Joi.string().required().label("Username"),
password: Joi.string().required().label("Password"),
};
abortEarly = {
abortEarly: false,
};
handleSubmit = (event) => {
event.preventDefault();
const errors = this.validate();
if (errors) return;
console.log("submitted");
};
validate = () => {
const result = Joi.validate(
this.state.account,
this.schema,
this.abortEarly
);
const errors = {};
if (!result.error) return false;
result.error.details.map((detail) => {
errors[detail.path[0]] = detail.message;
return detail.path[0];
});
// console.log(errors);
this.setState({ errors });
return true;
};
validateProperty = ({ name, value }) => {
const propertyTobeValidated = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(propertyTobeValidated, schema);
return error ? error.details[0].message : null;
};
handleChange = ({ currentTarget }) => {
const errors = { ...this.state.errors };
const error = this.validateProperty(currentTarget);
if (error) errors[currentTarget.name] = error;
else delete errors[currentTarget.name];
const account = { ...this.state.account };
account[currentTarget.name] = currentTarget.value;
this.setState({ account, errors });
};
render() {
const { account, errors } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
label="Username"
name="username"
value={account.username}
onChange={this.handleChange}
error={errors.username}
></Input>
<Input
label="Password"
name="password"
value={account.password}
onChange={this.handleChange}
error={errors.password}
></Input>
<button disabled={this.validate} className="btn btn-primary">
Login
</button>
</form>
</div>
);
}
}
export default LoginForm;
validate method should return boolean (true or false)

Lose state in React Js

I have an application with 2 inputs; name and password. Also, I have a save button that should change the state, using the values from the inputs, in the parent component.
But now, if I insert just one value in one input, I lose the state in the parent component. For example, if I type just name, and click save button, in the parent component I lose the password value, but I don't know why.
How to avoid this using my code?
import React, { useEffect } from "react";
import "./styles.css";
const Test = ({ user, setUser }) => {
const [u, setU] = React.useState("");
const [p, setP] = React.useState("");
function name(e) {
const a = e.target.value;
setU(a);
}
function password(e) {
const a = e.target.value;
setP(a);
}
function save() {
console.log(u);
setUser({ ...user, name: u, password: p });
}
return (
<div>
<input onChange={name} />
<input onChange={password} />
<button onClick={save}>save</button>
</div>
);
};
export default function App() {
const [user, setUser] = React.useState({
name: "",
password: ""
});
useEffect(() => {
setUser({ name: "john", password: "Doe" });
}, []);
return (
<div className="App">
<p>{user.name}</p>
<p>{user.password}</p>
<Test user={user} setUser={setUser} />
</div>
);
}
code link: https://stackblitz.com/edit/react-ytowzg
This should fix your issue:
function save() {
console.log(u);
if(u === '') {
setUser({ ...user, password: p });
} else if (p === '') {
setUser({ ...user, name: u });
} else {
setUser({ ...user, name: u, password: p });
}
}
So, now the state is conditionally updated based on the values of the input fields. The issue with your code is that you're always overwriting the state regardless of the input values.
i have a better proposition,instead of using a separate state variable for name ,password and percentage use a single state variable object
Test.js
import React, { useState } from "react";
import { InputNumber } from "antd";
import "antd/dist/antd.css";
const Test = ({ user, setUser }) => {
const [state, setState] = useState({
name: "",
password: "",
percentage: ""
});
function onChange(e, name) {
setState({
...state,
...(name === undefined
? { [e.target.name]: e.target.value }
: { [name]: e })
});
console.log(state);
}
function save() {
setUser({
...user,
...(state.name !== "" && { name: state.name }),
...(state.password !== "" && { password: state.password }),
...(state.percentage !== "" && { percentage: state.percentage })
});
}
return (
<div>
<input name='name' onChange={onChange} />
<input name='password' onChange={onChange} />
<InputNumber
defaultValue={100}
min={0}
max={100}
formatter={value => `${value}%`}
parser={value => value.replace("%", "")}
onChange={e => onChange(e, "percentage")}
/>
<button onClick={save}>save</button>
</div>
);
};
export default Test;
Updated CodeSandbox here

Found "Undefined in Document Collection" when try to fetch and update Collection from Firestore

i am still newbie and still self learning using react & firebase, please help me with this code.
I try to fetch and update data from firestore colection data using form with react & material-ui,
after i update useEffect function, no Error found it just not working as my expectation.
attached file firestore location picture
import React, { useState, useEffect } from 'react';
// material-ui
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Grid from '#material-ui/core/Grid';
import Typography from '#material-ui/core/Typography';
// firebase
import { useFirebase } from '../../../components/FirebaseProvider';
import { useDocument } from 'react-firebase-hooks/firestore';
import AppPageLoading from '../../../components/AppPageLoading';
import { useSnackbar } from 'notistack';
the function
function EditProduk({ match }) {
const { firestore, user } = useFirebase();
const { enqueueSnackbar } = useSnackbar();
const produkTraining = firestore.doc(`userdoc/${user.uid}/training/${match.params.trainingId}`);
const [snapshot, loading] = useDocument(produkTraining);
const [form, setForm] = useState({
name: '',
trainer: '',
price: 0,
description: ''
});
const [error, setError] = useState({
name: '',
trainer: '',
price: '',
description: ''
})
const [isSubmitting, setSubmitting] = useState(false);
useEffect(() => {
if (snapshot) {
setForm(currentForm => ({
...currentForm,
...snapshot.data()
}));
}
}, [snapshot]);
const handleChange = e => {
setForm({
...form,
[e.target.name]: e.target.value
})
setError({
...error,
[e.target.name]: ''
})
}
const validate = () => {
const newError = { ...error };
if (!form.name) {
newError.name = 'Training name must be filled';
}
if (!form.trainer) {
newError.trainer = 'trainer name must be filled';
}
if (!form.price) {
newError.price = 'price must be filled';
}
if (!form.description) {
newError.description = 'description must be filled';
}
return newError
}
const handleSubmit = async e => {
e.preventDefault();
const findErrors = validate();
if (Object.values(findErrors).some(err => err !== '')) {
setError(findErrors);
} else {
setSubmitting(true);
try {
await produkTraining.set(form, { merge: true });
enqueueSnackbar('Data has been saved', { variant: 'success' })
}
catch (e) {
enqueueSnackbar(e.message, { variant: 'error' })
}
setSubmitting(false);
}
}
if (loading) {
return <AppPageLoading />
}
return <div>
<Typography variant="h5" component="h1">Edit Training: {form.name}</Typography>
<Grid container alignItems="center" justify="center">
<Grid item xs={12} sm={6}>
<form id="produk-form" onSubmit={handleSubmit} noValidate>
<TextField
id="name"
name="name"
label="Training Name"
margin="normal"
required
fullWidth
value={form.name}
onChange={handleChange}
helperText={error.name}
error={error.name ? true : false}
disabled={isSubmitting}
/>
firestore rules
service cloud.firestore {
match /databases/{database}/documents {
match /userdoc/{uid} {
allow read: if request.auth.uid == uid;
allow write: if request.auth.uid == uid;
match /training/{trainingId}{
allow read, write: if request.auth.uid == uid;
}
}
}
}
please give me some advise
First of all if you want to test the firestore rules, there is a good option for that (firestore simulator, you can find it in the firebase consol) in which you can specify so many different parameter and test whether you can access data or not based on a specific scenario.
as far as I understand you want to fetch data from firestore, put it into a form, possibly edit the data and save it back to the database.
The error your are receiving has nothing to do with firestore or its rules, because it seems the form variable is undefined at the moment.
Try this:
async function EditProduk({ match }) {
...
const [snapshot, loading] = await useDocument(trainingDoc);
To remove the error you need to replace value={form.name} with value={form && form.name && form.name}
as well as
useEffect(() => {
if (snapshot.exists) {
setForm(snapshot.data());
}
}, [snapshot]);

Accept multiple input field in redux?

I'm learning redux and can able to update single input field but when there's more than 1 input field can't able to update state of both input field!
Here's my code please check it.
My main index.js:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import {FormReducer} from "./reducers/name";
import { createStore } from "redux";
import { Provider } from "react-redux";
const store = createStore(FormReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
My presentational and input field component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as actions from "./actions";
class App extends Component {
inputData = event => {
this.props.actions.addToDo({ name: event.target.value });
};
submitData = event => {
console.log(this.props.name);
// console.log(this.props.mobile);
event.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.submitData}>
FirstName:
<input
type="text"
name={this.props.name}
onChange={this.inputData}
/>
<input
type="text"
name={this.props.mobile}
onChange={this.inputData2}
/>
<button type="submit">Submit</button>
</form>
{this.props.name}
{this.props.mobile}
</div>
);
}
}
const mapStateToProps = state => ({
mobile: state.mobile,
name: state.name
});
const mapDispatchToProps = dispatch => {
return {
actions: bindActionCreators(actions, dispatch)
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
In my presentational component I want to make input acceptance such like array like [event.target.name]:event.target.value or something like this.
But when I try to do this with redux it's not working properly. Please check my code and give your input!
My reducer function:
let init = {
name: ''
}
export const FormReducer = (state = init, action) => {
switch (action.type) {
case "ADD_NAME":
return {...state, name:action.name, mobile:action.mobile}
default:
return state;
}
};
My action function which takes action on new input:
let init = {
name: ''
}
export const FormReducer = (state = init, action) => {
switch (action.type) {
case "ADD_NAME":
return {...state, name:action.name, mobile:action.mobile}
default:
return state;
}
};
And also as you can see I want to print both name and mobile both but it's only printing on which I'm working. Please give code so that I can able to print both simultaneously.
Thanks in advance!
It should be like this.
Use ecma6#computed property name [event.target.name]
inputData = event => {
this.props.actions.addToDo({ [event.target.name]: event.target.value });
};
EDIT :
in reducers,add this .
let init = {
name: { key : "name", value : "" },
mobile: { key: "mobile", value: "" },
};
reducer :
case "ADD_NAME":
console.log(action,state);
return {
...state,
name: {
...state.name, value: action.name||state.name.value
},
mobile: {
...state.mobile,
value: action.mobile || state.mobile.value
}
};
If you checked in render,both input field need name attribute which is not initialised and coming blank via props mapped in mapStateToProps.
So,you are not getting name attribute in event handler.
Need to initilise it.I assumed mobile and name.
1rst make state and save field inputs there like this.
state = {
username: "",
password: ""
}
where updateValue will save values from inputs to state
updateValue = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
<input type="text" name="username" value={this.state.username} onChange={(e) => this.updateValue(e)} />
<input type="password" name="password" value={this.state.password} onChange={(e) => this.updateValue(e)} />
then you can collect data from state and pass it to your function, for example like this login form.
submitData = event => {
const user = {
username: this.state.username,
password: this.state.password
}
// now pass this `user` to your action
event.preventDefault();
}

Resources