state in my react app get set to undefined - reactjs

Below is my code for a personal project where i can keep track of my monthly subscriptions, if i have to add a subscription i just have a add an object to an existing array. however for testing purposes when i tried to console.log(value.startDate) in handleSubmit it gives me undefined and causes further problems. How would i fix it?
import React from 'react';
import PropTypes from 'prop-types';
const List = () => {
const [ mylist, setList ] = React.useState([]);
const [ value, setValue ] = React.useState({ subscription: '', startDate: '', paymentTime: 0 });
const handleSubmit = (e) => {
console.log(value.startDate);
setList(mylist.push(value));
e.preventDefault();
};
const handleOnChange = (event) => {
setValue({ [event.target.name]: event.target.value });
};
return (
<div>
<div className="for_list">
<ul className="list">{mylist.map((obj) => <li key={obj.subscription}>{obj.subscription}</li>)}</ul>
</div>
<div className="for_form">
<form>
<input type="text" name="subscription" onChange={handleOnChange} value={value.subscription} />
<input type="text" name="startDate" onChange={handleOnChange} value={value.startDate} />
<input type="number" name="paymentTime" onChange={handleOnChange} value={value.paymentTime} />
</form>
</div>
<button onClick={handleSubmit}>Add Item</button>
</div>
);
};
// it just removes the error above.
List.propTypes = {
list: PropTypes.node
};
export default List;

You are replacing your state every time. This might be because of the miss in understanding the difference between setState in traditional class based React components and useState.
You need to append the value to the existing data. Something similar would work
const handleOnChange = (event) => {
setValue({ ...value, [event.target.name]: event.target.value });
};
The setState in class based components always accepts partial state and merges with the existing one. While useState setter function replaces the value you provide in the respective state.

On handleChange function you need to pass the old value of value
const handleOnChange = (event) => {
setValue({ ...value , [event.target.name]: event.target.value });
};

Related

The state does not change the first time, react

Why, when the user enters data into the form for the first time, 'users' remains an empty array, as it was, and only after the second time button is pressed, the data is written to 'setUser?
import Card from "../UI/Card";
import Button from "../UI/Button";
const UserForm = (props) => {
const [users, setUsers] = useState([]);
const [data, setData] = useState({ username: "", age: "" });
const submitHandler = (e) => {
e.preventDefault();
setUsers([...users, data]);
console.log("2", users);
props.getUsers(users);
};
return (
<Card className={classes.container}>
<div>Username</div>
<input
type="text"
onChange={(e) => {
setData({ ...data, username: e.target.value });
}}
></input>
<div>Age (Years)</div>
<input
type="number"
onChange={(e) => setData({ ...data, age: e.target.value })}
></input>
<Button onClick={submitHandler}>Add User</Button>
</Card>
);
};
export default UserForm;
....................
React State update takes time. So you need a useEffect hook with state as a argument in it. so whenever the state change, this hook triggers and perform your state related functions.
useEffect(() => {
console.log(users)
props.getUsers(users);
}, [users]);
it does change but how react works ?
when you update the value of one react hook and if this value is different from the previous one the component re-render with the new value of the hook.
in you code you are trying to :
console.log("2", users);
this is just before the component re-render so the new value is not available yet, but directly after submitHandler the component will re-render with new value of users
you can understand better if you try to console log() from inside your jsx
return (
<Card className={classes.container}>
{console.log('this is a new render and this is my users value :',users)}
</Card>
);
learn more here about React component lifecycle

setState on submit and not onChange

I have a form, where I use the input from the input fields to send to a back-end.
For example, I have a variable looking something like this:
const [data, setData] = useState([])
const [inputField, setInputField] = useState()
Then I have a form that looks something like this:
<form onSubmit={fetchData}>
<input type="number" value={value} onChange={(e) => setInputField(e.target.value)} />
<button type="submit">Fetch data</button>
</form>
The fetchData is given by:
function fetchData(e?: any) {
e?.preventDefault();
POST("/api", {
inputField: inputField,
}).then(async (response) => {
const json = await response.json();
setData({
retrievedData: json.retrievedData,
});
});
}
I have other forms as well, where this onChange updating is good, but for some input fields I don't need it do update/re-render before the actual submit button that triggers the form are clicked.
So how do I update the state of the inputField when the button is clicked, instead of now where it updates every time I write a new character in the input field ?
Try this
import {useRef } from "react";
export const Temp = () => {
const inputField = useRef(null);
const onBtnClick = () => {
alert(inputField?.current?.value);
};
return (
<div>
<input type="text" ref={inputField} />
<button type="submit" onClick={onBtnClick}>
Fetch data
</button>
</div>
);
};
You can use useRef hook for that.
const inputNumber = useRef();
<input
ref={inputNumber}
id="number"
placeholder="33xx"
type="number"
/>
Then on button click you can get the value like that
inputNumber.current?.value,
You don't need a state for that and you don't even need a ref. You can get the form values directly from the submit event (event.target.<input name>.value). You will need to add the name property to the <input /> to make it accessible from the event target. Please, find the example below:
function Form() {
const [data, setData] = React.useState();
const onSubmit = (e) => {
e.preventDefault();
const inputField = e.target.inputField.value;
POST("/api", {
inputField: inputField,
}).then(async (response) => {
const json = await response.json();
setData({
retrievedData: json.retrievedData,
});
});
};
return (
<form onSubmit={onSubmit}>
<input type="text" name="inputField" />
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(
<Form />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

How to pass data in react among different files or routes?

I'm new to react and after a beginners course of learning react. i decided to take a personal project of creating a monthly subscription app that'll help me keep track of all my subscriptions. However, i'm stuck at a point where i need to pass the state of one component into another but that other component is accessible through a route, btw i'm using reach router. is there any way that is possible. below is my code. So what i want to do basically is that when i click on of the subscriptions in my list, (say: Netflix) it should basically take me another page, and show me all the neccesary details regarding that subscription. Here i want state (mylist) to be passed.
App.js
const App = () => {
return (
<div className="container">
<Router>
<List path="/" />
<Details path="/details/:sub" />
</Router>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
List.js
const List = () => {
const [mylist, setList] = React.useState([]);
const [value, setValue] = React.useState({
subscription: "",
startDate: "",
paymentTime: 0,
});
const handleSubmit = (e) => {
mylist.push(value);
setList(mylist);
setValue({ subscription: "", startDate: "", paymentTime: 0 });
e.preventDefault();
};
const handleOnChange = (event) => {
setValue({ ...value, [event.target.name]: event.target.value });
};
return (
<div>
<div className="for_list">
<ul className="list">
{mylist.map((obj) => (
// <Link to={`/details/${obj.subscription}`} key={obj.subscription}>
<li key={obj.subscription}>{obj.subscription}</li>
/* </Link> */
))}
</ul>
</div>
<div className="for_form">
<form>
<input
type="text"
name="subscription"
onChange={handleOnChange}
value={value.subscription}
/>
<input
type="date"
name="startDate"
onChange={handleOnChange}
value={value.startDate}
/>
<input
type="number"
name="paymentTime"
onChange={handleOnChange}
value={value.paymentTime}
/>
</form>
</div>
<button onClick={handleSubmit}>Add Item</button>
</div>
);
};
export default List;
Details.js
const Details = (props) => {
return (
<div>
Dunno what to do ;(
</div>
);
};
export default Details;
React Router uses location objects. One of the properties of a location object is state. You can pass some data field to state, then in your Details page, you will use these data fields to fetch data of each corresponding subscription.
In your List.js:
...
const handleSubmit = (e) => {
e.preventDefault();
mylist.push(value);
setList(mylist);
setValue({ subscription: "", startDate: "", paymentTime: 0 });
props.histoty.push({
pathname: '/details/netflix',
state: {
id: 'netflix-id',
}
})
};
...
export default withRouter(List);
Then in your Details.js:
import React, { useEffect, useState } from 'react'
import { withRouter } from 'react-router-dom'
const Details = (props) => {
const [subInfo, setSubInfo] = useState({})
useEffect(() => {
fetch(`your-server-api/${props.state.id}`).then(res => res.json()).then(data => setSubInfo(data))
}, [])
return (
<div>
...
</div>
);
};
export default withRouter(Details);
One thing to keep in mind is that there will be no state if a user navigates directly to the page, so you will still need some mechanism to load the data when it does not exist.

Reactjs - TypeError: Cannot read property 'value' of null

I am new and still practicing in reactjs. I am having a problem in my input form, whenever I type any key I always get TypeError: Cannot read property 'value' of null
this is my code:
import React, { useState } from 'react';
export default function FormPractice() {
const [isDefault, setIsDefault] = useState('');
const [balance, setBalance] = useState({amount: null});
return (
<div className="input">
<input placeholder="enter balance here" onChange={e => setBalance(form => ({...form, [e.target.value]: e.target.value}))} value={isDefault}/>
</div>
)
}
Thanks for the help.
React re-uses events. When you use a functional update, by the time the function is called, the event has already been wiped for reuse. To use the event within the functional update you would need to call e.persist() which will take that event out of the reuse pool and let it keep its values.
To keep it inline, it would look like this:
onChange={e => {e.persist(); setBalance(form => ({...form, [e.target.value]: e.target.value}))}}
Or to make it more readable, move it into its own function:
const onChange = (e) => {
e.persist();
setBalance(form => ({...form, [e.target.value]: e.target.value}));
}
However, the simplest available solution would be to not use the functional update at all. You are replacing state values, but you are not setting any values derived from the previous state. So it is safe to use a normal update:
onChange={e => setBalance({...balance, [e.target.value]: e.target.value})}
Now the event reuse is a non-issue.
Side note: [e.target.value]: e.target.value this doesn't really make sense. You're setting an object key with the name of the new value to the same value.
It seems like maybe you've seen [e.target.name]: e.target.value before and modified it. I would suggest using the name, and then giving the input the name of the property you want to update.
Here is a simplified example:
const {useState, useEffect} = React;
const Example = () => {
const [state, setState] = useState({ input1: '' });
const onChange = (e) => {
setState({[e.target.name]: e.target.value});
}
return (
<input
name="input1"
placeholder="enter balance here"
onChange={onChange}
value={state.input1}
/>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Saving JS State to an unordered list

class App extends Component {
constructor() {
super()
this.state = {
firstName: ""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
firstName: event.target.value
})
}
render() {
return (
<form>
<input type="text" placeholder="First Name" onChange={this.handleChange} />
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
Hello all, I am currently studying React and seem to be having a hard time grasping all of it. The code that I have here works in that it will show in browser what the user is typing in the input box. What I cannot seem to figure out or get to work, is mapping what is typed in the input to stay on the screen. I.e. when I hit enter, it refreshes and the name goes away. I am trying to now create an unordered list to keep each name displayed on the screen. Any help or links would be greatly appreciated. Thank you
Just add new function (this describe what should be after submit this form) in this case You use:
event.preventDefault() -
The Event interface's preventDefault() method tells the user agent
that if the event does not get explicitly handled, its default action
should not be taken as it normally would be
onSubmit(event){
event.preventDefault()
}
and on form:
<form onSubmit={this.onSubmit}>
To create unordered list use something like this (credit for Robin Wieruch):
import React from 'react';
const initialList = [
'Learn React',
'Learn Firebase',
'Learn GraphQL',
];
const ListWithAddItem = () => {
const [value, setValue] = React.useState('');
const [list, setList] = React.useState(initialList);
const handleChange = event => {
setValue(event.target.value);
};
const handleSubmit = event => {
if (value) {
setList(list.concat(value));
}
setValue('');
event.preventDefault();
};
return (
<div>
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">Add Item</button>
</form>
</div>
);
};
export default ListWithAddItem;

Resources