Random set key is being cached? - reactjs

I am following a tutorial for React/Redux (https://youtu.be/WQMglp-JASk). This part of the tutorial adds a simple form to the page and adds each entry into another section. Here is the code for adding the new info to the page:
addNinja = (ninja) => {
ninja.id = Math.random();
let ninjas = [...this.state.ninjas, ninja];
this.setState({
ninjas: ninjas
});
};
The ninja object is being passed to this method from this code:
state = {
name: null,
age: null,
belt: null
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
});
};
handleSubmit = (e) => {
e.preventDefault();
this.props.addNinja(this.state);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" onChange={this.handleChange}/>
<label htmlFor="age">Age:</label>
<input type="text" id="age" onChange={this.handleChange}/>
<label htmlFor="belt">Belt:</label>
<input type="text" id="belt" onChange={this.handleChange}/>
<button>Submit</button>
</form>
</div>
);
}
And the component JSX that is reused on submit:
<div className="ninja" key={ninja.id}>
<div>Name: {ninja.name}</div>
<div>Age: {ninja.age}</div>
<div>Belt: {ninja.belt}</div>
<button onClick={() => {deleteNinja(ninja.id)}}>x</button>
</div>
ninja.id is the key for the element being added.
The first submit works fine, any subsequent submits use the same key generated by Math.random() on the first submit. I also see the warning Encountered two children with the same key in the console for each subsequent submit.
When I console log the random number it is a different each time, but ninja.id is not being set to the new random number.
I assume caching is at play here but can someone please explain why this might be happening?

Well, the way you are adding the id for your ninja you are mutating your state which is a big no-no for react. So the following:
let ninjas = [...this.state.ninjas, ninja];
should be:
let ninjas = [...this.state.ninjas,
{
id: Math.random(),
name: this.state.name,
age: this.state.age,
belt: this.state.belt
}
];
There are definitely better ways for using as id for iterating items within an array such as https://www.npmjs.com/package/uuid

Ok, so I actually got this working myself but I would love for someone to tell me what the issue is.
To get it working, the AddNinja method should be:
addNinja = (ninja) => {
let ninjas = [...this.state.ninjas, {
"name": ninja.name,
"age": ninja.age,
"belt": ninja.belt,
"id": Math.random()
}];
this.setState({
ninjas: ninjas
});
};
I have no idea why this works and the other version doesn't so any help would be appreciated :)

Related

Display Times available based on the Date in a form (looking for solution with useReducer)

Honestly i cant wrap my Head around how to create and write the Function of updateTimes which will update the Times option in my Form based on the selected date. Those are my instruction:
Step 2: Update BookingForm to display available times based on the selected date
The next step is to prepare the available times to be updated based on the date the user has selected. To do this, you will change the availableTimes state to a reducer.
In the Main component, create a function named updateTimes which will handle the state change. This function will change the availableTimes based on the selected date. For now, the function can return the same available times regardless of the date.
Next, create a function called initializeTimes which will create the initial state for the availableTimes.
Then, change availableTimes to a reducer using the useReducer function and provide the two previous functions as parameters.
Update the BookingForm component to dispatch the state change when the date form field is changed.
This is the Code ive been able to get to until now:
const formReducer= (state, event) => {
return {
...state,
[event.name]: event.value
}
}
function Bookingform(props){
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState();
const times = [
{value: "17:00", text: "17:00" },
{value: "18:00", text: "18:00" },
{value: "19:00", text: "19:00" },
{value: "20:00", text: "20:00" },
{value: "21:00", text: "21:00" },
{value: "22:00", text: "22:00" },
]
const handleSubmit = (e) => {
e.preventDefault();
setSubmitting(true);
props.onSubmit(formData);
setTimeout (() => {
setSubmitting(false);
}, 3000)
};
const handleChange = event => {
const isCheckbox = event.target.type === "checkbox";
setFormData({
name: event.target.name,
value: event.target.value,
value: isCheckbox ? event.target.checked : event.target.value,
});
}
return (
<div className="Wrapper">
<h1>Reserve a Table</h1>
<form onSubmit={handleSubmit}>
<fieldset>
<field name="field" value={field}>
<label>
<p>Time</p>
<select name="Time" onChange={handleChange}>
{times.map((option, index) => (
<option key={index} value={option.value}>
{option.text}
</option>
))}
</select>
</label>
<label>
<p>Date</p>
<input type="date" name="Date" onChange={handleChange}/>
</label>
<label>
<p>Guests</p>
<input type="number" name="Count" min="1" max="9" onChange={handleChange} step="1"/>
</label>
<label>
<p>Choose Occasion</p>
<select name="Occasion" onChange={handleChange}>
<option>Anniversary</option>
<option>Birthday</option>
</select>
</label>
<button type="submit">Submit</button>
</field>
</fieldset>
</form>
</div>
)
}
im quite stuck at this point and cant wrap my Head around the Solution, a explanation for my Problem would be great ! thanks in advance.
I was trying to write a function which is able to Update the Times with the Selected options inside Date, but anytime im trying to write it my whole Site actually breaks. I dont get to much smarter from reading the Docs tho... i do understand pretty much how the useReducer Works but i cant find a way to implement it in this Situation.

Am I supposed to have this message in my console while I'm trying to "see" my input fields?

can someone tell me why am I getting this message in my console while I'm trying to send my data to my backend but the only thing that I get when I fill the title (for instance: Hello) is :
:
SyntheticBaseEvent {_reactName: 'onChange', _targetInst: null, type: 'change', nativeEvent: InputEvent, target: input#title.p-1.border-2.mx-2.rounded-sm, …}
Plus, I'm not able to see the data from my Dropdown and my checkboxe I can see title and the date selected but not the checkbox and the Dropdown...
I was expected to get :
{title:"H", date:"", choice: "", comment:""}
...
{title:"Hello", date:"", choice: "", comment:""}
... ( after chosing the date and the choice):
{title:"Hello", date:"2022-05-27", choice: true (if checkboxe is selected) comment: "Good"}...
Account:
export default function Account() {
const [data, setData] = useState({
title: "",
date: "",
choice: "",
comment:""
});
const COMMENT = [
{ label: "Good", value: "Good" },
{ label: "Medium", value: "Medium" },
{ label: "Bad", value: "Bad" }
];
function onSubmit(e) {
e.preventDefault();
axios
.post("", {
title: data.title,
date: data.date,
choice: data.choice
comment:data.comment
})
.then((res) => {
console.log(res.data);
});
}
function handleSubmit(e) {
const newData = { ...data };
newData[e.target.id] = e.target.value;
setData(newData);
console.log(e);
}
function Checkbox({ value }) {
const [checked, setChecked] = useState(value);
return (
<label>
<input
type="checkbox"
checked={checked}
onChange={(e) => setChecked(checked => !checked)}
style={{
transform: "scale(1.2)",
}}
/>
{value}
</label>
);
}
return (
<>
<form onSubmit={(e) => onSubmit(e)}>
<label>
Comment :
<Dropdown
options={COMMENT}
isMulti={false}
value={data.comment}
onChange={(e) => handleSubmit(e)}
/>
</label>
Choice : <Checkbox value={0} />
<label >
Title:
<input
type="text"
value={data.title}
placeholder="Title"
onChange={(e) => handleSubmit(e)}
id="title"
/>
</label>
<label >
Date:
<div>
<input
type="date"
value={data.date}
onChange={(e) => handleSubmit(e)}
id="date"
/>
</div>
</label>
<button>Submit</button>
</form>
</>
);
}
My goal is to be able when I click on 'Confirm' button (StepperControl.jsx) to send all the data to my api (and not each time i click on Next, but I'm aware that I will have to put an url, for now it will not work but that's fine, when i will give it one, it will work afterwards)
Here is my code
The problem is that you console.log the actual change event. Change console.log(e); in handleSubmit method to console.log(newData) to see the new data. Also to avoid confusion, I suggest to change this method name (handleSubmit method), as it is not a submit event handler but a change event handler - the submit method that sends the data is onSubmit.
Another issue I noticed - choice dropdown is not controlled so data.choice will never change.
The problem is that you're console.loging e. You need to console.log data or newData to get the output that you are looking for. e is the click event, so it shows all of the properties on the event (target, etc.), not the actual data.

React form behaving strange with this.handleChange and Axios

SOLVED
I have a React/Typescript app which has a form that I'm using in a website that renders movies we've seen from a Mongo database. I have it set up so that we can edit one of the movies in the database through an edit form. Everything goes very smoothly but for the first field which is the movie title. Whenever I change that field it immediately stops me from doing anything else and I have to click the field again.
I.E. initial title: "Foo" -> "Fo" -> click -> "F" -> click -> "" -> click -> "B" -> click -> "Ba" -> click -> "Bar".
All other fields in the don't have this behavior. As far as I can tell all input forms are the same (type: text) and there should not be any difference. I can type full sentences without a pause in any other input field but no matter what I do, the first one doesn't allow me to. Why?
const EditBody = styled.div`{blablabla}`;
export interface Props {
match: {
params: {
title: string
}
},
history: any
}
type MovieObject = {
comments: string,
imdb: string,
mtc: string,
rtc: string,
smn: string,
title: string
}
class Edit extends Component<Props, { movie: any }> {
constructor(props: any) {
super(props);
this.state = {
movie: [
{
comments: '',
imdb: '',
mtc: '',
rtc: '',
smn: '',
title: '',
pictureUrl: '',
__v: Number,
_id: String
}]
};
this.handleChange = this.handleChange.bind(this);
};
componentDidMount() {
this.callServerForDatabase()
.then(data => this.setState({ movie: data }))
.catch(err => console.log(err));
};
callServerForDatabase = async () => {
const { match: { params } } = this.props;
const response = await fetch(`/edit/${params.title}`);
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
handleChange(e: any) {
e.preventDefault();
const name = e.target.name;
const value = e.target.value;
let thisStateMovieClone: MovieObject[] = [...this.state.movie];
let updatedMovieObj: MovieObject = {
...thisStateMovieClone[0],
[name]: value
}
thisStateMovieClone[0] = updatedMovieObj;
this.setState({
movie: thisStateMovieClone
})
}
handleUpdate = (e: any) => {
e.preventDefault();
axios.post(`/edit/${this.state.movie.title}`, this.state.movie)
.then(() => this.props.history.push("/database"));
};
handleDelete = (e: any) => {
e.preventDefault();
axios.post('/delete', this.state.movie)
.then(() => this.props.history.push("/database"));
};
render() {
return (
<EditBody>
<h2>Update Movie Data</h2>
{this.state.movie.map((movie: any) =>
<div key={movie.title} className="container">
<form action={`/edit/${movie.title}?_method=PUT`} method="PUT">
<div className="inputrow">
<p className="description">Movie Title
<input className="input" type="text" value={movie.title} onChange={this.handleChange} name="title" /></p></div>
<div className="inputrow">
<p className="description">Rotten Tomatoes Score
<input className="input" type="text" value={movie.rtc} onChange={this.handleChange} name="rtc" /></p></div>
<div className="inputrow">
<p className="description">Metacritic Score
<input className="input" type="text" value={movie.mtc} onChange={this.handleChange} name="mtc" /></p></div>
<div className="inputrow">
<p className="description">IMDB Score
<input className="input" type="text" value={movie.imdb} onChange={this.handleChange} name="imdb" /></p></div>
<div className="inputrow">
<p className="description">Scary Movie Night Score
<input className="input" type="text" value={movie.smn} onChange={this.handleChange} name="smn" /></p></div>
<div className="inputrow">
<p className="description">Comments
<input className="input" type="text" value={movie.comments} onChange={this.handleChange} name="comments" /></p></div>
<button id="submitButton" type="submit" onClick={this.handleUpdate}><h2>Update Movie</h2></button> or <button id="submitButton" type="submit" onClick={this.handleDelete}><h2>Delete Movie</h2></button>
</form>
</div>
)}
</EditBody>
)
}
};
On top of that I have a problem with the axios request to the server. It works (i.e. the movie gets updated or deleted from the DB) but the this.props.history.push either doesn't work (right now) or when it does, it's faster than the server response so it re-routes back to a /database page which hasn't received the updated information yet. Only after a refresh does /database show the correct information. I've tried async/await, setTimeout, this.props.context.push, and as you an see a .then() after the returned promise. Nothing works. I think because this.history pushes to an old version of the page, instead of actually loading the page again?
Both these problems are driving me nuts! Any help would be appreciated. If someone needs to see more code let me know.
EDIT:
The componentDidMount() and callServer.. functions are because we get to this /edit page by clicking on a movie that we want to edit. So it gets the information for that movie from the server after I click it on another page.
For anyone reading: it was because I had the {movie.title} also as the key of the container div. This meant it re-rendered the div each time, since the key also changed. I created an index parameter and assigned the key to that which solves the rendering problem. Now just the axios problem left..
{this.state.movie.map((movie: any, index: any) =>
<div key={index} className="container"> //CHANGED KEY VALUE
<form action={`/edit/${movie.title}?_method=PUT`} method="PUT">
// WHICH COLLIDED WITH THIS VALUE
<div className="inputrow">
<p className="description">Movie Title
<input className="input" type="text" value={movie.title} onChange={this.handleChange} name="title" /></p></div>
And also solved the axios problem. Updated through this.setState and added a conditional in the render function. Now it works with . For anyone happening upon this question.

System throws exception and display playerProfile.map is not a function

I would like to display data received in the Player Profile fields. Started with name field, it displays name data, but while trying to edit the name text field, system throws following exception, TypeError: playerProfile.map is not a function. I have wrapped the fetch call inside the arrow function. Can someone please advise on what is the root cause of this error.
Note: At the moment I have received the value for name field only, need to display for other fields and still need to work on handleSubmit()
Detailed error message from console:
Uncaught TypeError: playerProfile.map is not a function
at Profile (Profile.js:34)
at renderWithHooks (react-dom.development.js:14803)
at updateFunctionComponent (react-dom.development.js:17034)
at beginWork (react-dom.development.js:18610)
at HTMLUnknownElement.callCallback (react-dom.development.js:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
at invokeGuardedCallback (react-dom.development.js:292)
at beginWork$1 (react-dom.development.js:23203)
at performUnitOfWork (react-dom.development.js:22157)
My Sample Code
const [playerProfile, setPlayerProfile] = useState([]);
const handleSubmit = (e) => {
e.preventDefault()
}
const onChange = (e) => {
e.persist();
setPlayerProfile({ ...playerProfile, [e.target.name]: e.target.value });
}
useEffect(() => {
const fetchData = async () => {
try {
const res = await Axios.get('http://localhost:8000/service/profile')
setPlayerProfile(res.data.playerProfile);
} catch (e) {
console.log(e);
}
}
fetchData();
}, []);
return (
<div className="register_player_Twocolumn_layout_two">
<form onSubmit={handleSubmit} className="myForm">
{
playerProfile.map(({ id, image, name, email, position, privilege, password }) =>(
<div>
<div key={id} className="formInstructionsDiv formElement">
<h2 className="formTitle">Player Profile</h2>
<div className="register_profile_image">
<input id="profilePic" name="photo" type="file"/>
</div>
<div className="previewProfilePic" >
<img alt="" error="" name="previewImage" className="playerProfilePic_home_tile" src=""></img>
</div>
</div>
<div className="fillContentDiv formElement">
<label>
<input className="inputRequest formContentElement" name="name" type="text" key={name} value={name} onChange={onChange}/>
</label>
<label>
<input className="inputRequest formContentElement" name="email" type="text"/>
</label>
<label>
<div className="select" >
<select name="privilege" id="select">
<option value="player">PLAYER</option>
<option value="admin">ADMIN</option>
</select>
</div>
</label>
<label>
<input className="inputRequest formContentElement" name="password" type="password"/>
</label>
</div>
<div className="submitButtonDiv formElement">
<button type="submit" className="submitButton">Save</button>
</div>
</div>
))
}
</form>
</div>
);
TLDR: Check the Sandbox
#soccerway, based on our comments pointed out as per typos in you approach, here is some code that attempts to fix them. The link to the Live Codesandbox
SOME Context
When you define you playerProfile component state, you initialize it as an array, successfully update it from the server as an array but mess it up in the input onChange handler. Let's say you type s in the name input. With this...
setPlayerProfile({ ...playerProfile, [e.target.name]: e.target.value });
...you are transforming the playerProfile form this array.
// Fetched playerProfile from the api.
playerProfile = [
{
name: "David",
email: "david#testmail.com",
phonenumber: null,
id: 5,
privilege: "PLAYER",
photo: "C:\\fakepath\\city.JPG",
position: "FORWARD",
updatedAt: "2020-05-25T11:02:16.000Z"
},
// Extra profile put to have a solid example
{
name: "Goriath",
email: "goriath#testmail.com",
phonenumber: null,
id: 5,
privilege: "PLAYER",
photo: "C:\\fakepath\\goriath.JPG",
position: "MIDI",
updatedAt: "2020-05-26T11:02:16.000Z"
},
]
// To This Object
playerProfile = {
0: {
name: "David",
email: "david#testmail.com",
phonenumber: null,
id: 5,
privilege: "PLAYER",
photo: "C:\\fakepath\\city.JPG",
position: "FORWARD",
updatedAt: "2020-05-25T11:02:16.000Z"
},
1: {
name: "Goriath",
email: "goriath#testmail.com",
phonenumber: null,
id: 6,
privilege: "PLAYER",
photo: "C:\\fakepath\\goriath.JPG",
position: "MIDI",
updatedAt: "2020-05-26T11:02:16.000Z"
},
name: Davids"
}
Like you see, you cannot map over an object unless if you get its keys or entries, in which case the approach would still be invalid by the second element in the object.
The other issue is that you are trying to update an object, and directly appending it to the array/object. If the update is successful, this will result into duplicate data save for the name. You need to find the old object in state and update it, then totally replace it. That would be fine if your data was normalized, like saved by keys initially. Something like this...
data= {
playerProfilesById = {
5: { // Player ID is the key
name: "David",
email: "david#testmail.com",
phonenumber: null,
id: 5,
privilege: "PLAYER",
photo: "C:\\fakepath\\city.JPG",
position: "FORWARD",
updatedAt: "2020-05-25T11:02:16.000Z"
},
6: {
name: "Goriath",
email: "goriath#testmail.com",
phonenumber: null,
id: 6,
privilege: "PLAYER",
photo: "C:\\fakepath\\goriath.JPG",
position: "MIDI",
updatedAt: "2020-05-26T11:02:16.000Z"
},
},
playerProfileIds=[5,6]
}
That way its easy to update playerProfilesById with your approach, with the [e.target.id](assuming you're passing the input tag it's id) not [e.target.name], while using the playerProfileIds to map over the items in the jsx.
However if you don't have control over the api data format, you could instead make sure you update your handler to receive an id(assuming the id is unique) from the onChange, use that id to find the profile in the array.
While finding, you could keep the element array Index, and use it to directly target and update the array. (Commented out approach in handler)
Or you could just map over the entire array and update the profile that changed, then use that data to eventually update state.
Below is the full approach.
import React, { useState, useEffect } from "react";
// import axios from "axios";
/* Assuming your api returns data in the follwoing format... */
const fakeAPICall = () => {
// CALL TO AXIO MUTED
// const res = await axios.get("http://localhost:8000/service/profile");
// NOTE: Please normalize this data so it's easy to update
// READ ABOUT: https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape
const data = {
playerProfile: [
{
name: "David",
email: "david#testmail.com",
phonenumber: null,
id: 5,
privilege: "PLAYER",
photo: "C:\\fakepath\\city.JPG",
position: "FORWARD",
updatedAt: "2020-05-25T11:02:16.000Z"
},
{
name: "Goriath",
email: "goriath#testmail.com",
phonenumber: "1234345234",
id: 6,
privilege: "PLAYER",
photo: "C:\\fakepath\\goriath.JPG",
position: "MIDFIELDER",
updatedAt: "2020-05-26T11:02:16.000Z"
}
]
};
return { data };
};
const PlayerProfile = () => {
// Note that your player profile is defined as an array in state.
// Remember to always keep it that way when updating it.
const [playerProfile, setPlayerProfile] = useState([]);
const handleSubmit = e => {
e.preventDefault();
};
// Pass the id to the handler so you will know which item id changing.
const handleChange = (e, id) => {
e.persist();
let itemIndex;
const targetPlayer = playerProfile.find((player, index) => {
console.log({ player, id, index });
itemIndex = index; // Track the index so you can use it to update later.
return player.id === id;
});
console.log({ targetPlayer, id, e });
const editedTarget = {
...targetPlayer,
[e.target.name]: e.target.value
};
const tempPlayers = Array.from(playerProfile);
tempPlayers[itemIndex] = editedTarget;
/*
// Alternatively:: you can just map over the array if you dont want to track the index
const tempPlayers = playerProfile.map((profile, index) => {
return profile.id === id ? editedTarget : profile;
});
*/
setPlayerProfile(tempPlayers);
};
useEffect(() => {
const fetchData = async () => {
try {
// const res = await axios.get("http://localhost:3000/api/products");
const res = await fakeAPICall();
console.log({ response: res });
setPlayerProfile(res.data.playerProfile);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
console.log({ "⚽: playerProfile": playerProfile });
return (
<div className="register_player_Twocolumn_layout_two">
<h1>CAPTURE PLAYER PROFILE</h1>
<p>Form to capture player Profile</p>
<hr />
<form onSubmit={handleSubmit} className="myForm">
{playerProfile.map(
({ id, image, name, email, position, privilege, password }) => (
<div key={id}>
{/*2. Also put the key on the outer div in the map above */}
<div className="formInstructionsDiv formElement">
<h2 className="formTitle">Player Profile</h2>
<div className="register_profile_image">
<input id="profilePic" name="photo" type="file" />
</div>
<div className="previewProfilePic">
<img
alt=""
error=""
name="previewImage"
className="playerProfilePic_home_tile"
src=""
/>
</div>
</div>
<div className="fillContentDiv formElement">
<label>
NAME
<input
className="inputRequest formContentElement"
name="name"
type="text"
// key={name} // Remove this key or renmae it to id. Since name changes on rerender, it confuses react that the key is different and forces the element to toose focus
value={name}
onChange={e => handleChange(e, id)} // Pass the ID form here.
/>
</label>
<label>
<input
className="inputRequest formContentElement"
name="email"
type="text"
/>
</label>
<label>
<div className="select">
<select name="privilege" id="select">
<option value="player">PLAYER</option>
<option value="admin">ADMIN</option>
</select>
</div>
</label>
<label>
<input
className="inputRequest formContentElement"
name="password"
type="password"
/>
</label>
</div>
<div className="submitButtonDiv formElement">
<button type="submit" className="submitButton">
Save
</button>
</div>
</div>
)
)}
</form>
</div>
);
};
export default function App() {
return (
<div className="App">
<PlayerProfile />
</div>
);
}
PS: When you are mapping over items, each direct wrapper expects a unique key prop, that way react can know which component exactly changed to a avoid re-renders. In your approach, you're assigning the key to the input deep in the tree. Move it up to the outer most div wrapper.
Also make sure that whatever item you use as the key is unique, otherwise the items will keep loosing focus on updates if the key changes. For example in your code, the name is being changed but you are using it as the input. This results into a new key meaning you are working on a new element, eventually loosing focus on that input.
This might be one of your problem
<label>
<input className="inputRequest formContentElement"
name="name" type="text" key={name} value={name}
onChange ={onChange}/>
</label>
The value "name" of the name property of the input tag is not in the array of playerProfile.
I think it should be:
<label>
<input className="inputRequest formContentElement"
name={name} type="text" key={name} value={name}
onChange ={onChange}/>
</label>
that means your problem should be here
setPlayerProfile({ ...playerProfile, [e.target.name]: e.target.value });
playerProfile was an array, but the above line set's it to an object that's y it's throwing the error
This might work:
setPlayerProfile([ ...playerProfile, [e.target.name]: e.target.value ]);

React. Can I get rid of ref in favor of onChange?

According to React spec:
https://reactjs.org/docs/refs-and-the-dom.html
"There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
Avoid using refs for anything that can be done declaratively."
That's why I'm not so sure for now, whether I used ref properly or not in this case:
export let FormInput = createReactClass({
handleInput(e){
e.preventDefault();
const product = this.name.value;
const red = this.red.checked;
this.props.addProduct(product,red);
this.inputForm.reset();
},
render(){
return(
<form className="prod_input" ref={x => this.inputForm = x} onSubmit={this.handleInput}>
<input type="text" ref={x => this.name = x} placeholder="Product name"/>
<input type="checkbox" ref={x => this.red = x} value="true"/>
<input type="submit" hidden/>
</form>
)
}
})
If not, how could it be rearranged in order to replace ref to onChange={}?
You can avoid using refs by making your input fields controlled. Here is an example:
export let FormInput = createReactClass({
constructor(props){
super(props);
this.state = {
name: '',
};
}
handleNameChange(updatedName){
this.setState({ name: updatedName });
}
handleInput(e){
e.preventDefault();
const product = this.state.name;
// ...
this.props.addProduct(product);
this.inputForm.reset();
},
render(){
return (
<form className="prod_input" ref={x => this.inputForm = x} onSubmit={this.handleInput}>
<input type="text" value={this.state.name} placeholder="Product name" onChange={e => this.handleNameChange(e.target.value)} />
// ...
</form>
);
}
})
The idea is to keep the value of your fields in your local state. Then you can set the value of each of your fields to the value currently stored in your state, and attach an onChange handler that updates this value every the user types something. When you submit your form, all your values are available directly in your component's state.

Resources