Cannot read property 'map' of undefined react 17 - reactjs

Currently, I started learning to react in the Udemy course "react-for-the-rest-of-us".
now, I'm trying to approach the state hook from the child component, and I get the above error.
my target is to add another element to the state by getting it's values from the user input
this is the parent component:
import React, { useState } from "react";
import AddPetForm from "./FormPet";
function Header(props) {
const [pets, setPets] = useState([
{ name: "Meowsalot", species: "cat", age: "5", id: 123456789 },
{ name: "Barksalot", species: "dog", age: "3", id: 987654321 },
{ name: "Fluffy", species: "rabbit", age: "2", id: 123123123 },
{ name: "Purrsloud", species: "cat", age: "1", id: 456456456 },
{ name: "Paws", species: "dog", age: "6", id: 789789789 },
]);
const pet = pets.map((pet) => (
<Pet name={pet.name} species={pet.species} age={pet.age} id={pet.id} />
));
return (
<div>
<LikedArea />
<TimeArea />
<ul>{pet}</ul>
<AddPetForm set={setPets} />
</div>
);
}
function Pet(props) {
return (
<li>
{props.name}is a {props.species} and is {props.age} years old
</li>
);
}
and this is the child component:
import React, { useState } from "react";
function AddPetForm(props) {
const [name, setName] = useState();
const [species, setSpecies] = useState();
const [age, setAge] = useState();
console.log(props.set);
function handleSubmit(e) {
e.preventDefault();
props.set((prev) => {
prev.concat({ name: name, species: species, age: age, id: Date.now() });
setName("");
setSpecies("");
setAge("");
});
}
return (
<form onSubmit={handleSubmit}>
<fieldset>
<legend>Add New Pet</legend>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={species}
onChange={(e) => setSpecies(e.target.value)}
placeholder="species"
/>
<input
value={age}
onChange={(e) => setAge(e.target.value)}
placeholder="age in years"
/>
<button className="add-pet">Add Pet</button>
</fieldset>
</form>
);
}
export default AddPetForm;

You aren't returning the new concatenated array. So when you call props.set it should be this:
props.set((prev) => {
setName("");
setSpecies("");
setAge("");
return prev.concat({ name: name, species: species, age: age, id: Date.now() });
});
If you don't return anything, then technically the return value is undefined, so that's what it sets the state to. Then you get the error when you try to .map it

Related

How to create react form with dynamic fields fetched from api

I want to create a react form with dynamic inputs, those inputs are fetched from server.
I fetched the data and I have displayed the form but the problem was how to get the values of the inputs with the onChange method
Use a Usestate hook and assign the value fetched from the API there
eg:-
const [value,setValue] = useState(fetch());
function fetch(){
//API call...
//return the value got from the API
}
Now in form input field you can use this as value and have an onChange and change the value state.
eg:-
<input value={value.name} name="name" onChange={(e)=>handlechange(e)} />
in handlechange :
const handlechange = (e)=>{
const {name,value} = e.target;
setValue({...value,[name]:value});
};
After this you can have an useEffect, which will POST the value to the backend using another API for every changes made in the form
Here is the code sandbox link:https://codesandbox.io/s/blazing-moon-wxp4w7?file=/src/App.js
after some researches on stackoverflow here is my solution:
const [fields, setFields] = useState([{field:"", value:""}]);
useEffect(()=>{
if(fetchedData){
setFields(fetchedData.map((item)=>{
var rObj ={field:"", value:""};
rObj["field"] = item;
return rObj;
}));
}
},[fetchedData])
const handleChange = ( index, event ) => {
if(fields) {
let data = [...fields];
data[index]["value"] = event.target.value;
setFields(data);
}
}
/////////////////////////////////////////
{fetchedData.map((field, index) => (
<div key={index}>
<input
required
type="text"
name={field}
value={fields.field}
placeholder= {field}
onChange={event=>{handleChange(index, event)}}
/>
</div>
))}
Here is a sample code:
const GenerateForm() => {
const [formInputs, setFormInputs] = useState({});
const handleInputChange = event => {
const { name, value } = event.target;
setFormInputs({ ...formInputs, [name]: value });
};
return (
<form>
{formData.map(input => {
if (input.type === "text" || input.type === "number") {
return (
<div key={input.name}>
<label htmlFor={input.name}>{input.label}:</label>
<input
type={input.type}
name={input.name}
value={formInputs[input.name] || ""}
onChange={handleInputChange}
/>
</div>
);
} else if (input.type === "select" || input.type === "radio") {
return (
<div key={input.name}>
<label>{input.label}:</label>
{input.options.map(option => (
<div key={option.value}>
<input
type={input.type}
name={input.name}
value={option.value}
checked={formInputs[input.name] === option.value}
onChange={handleInputChange}
/>
{option.label}
</div>
))}
</div>
);
}
return null;
})}
</form>
);
}
const formData = [
{
name: "firstName",
label: "First Name",
type: "text"
},
{
name: "lastName",
label: "Last Name",
type: "text"
},
{
name: "age",
label: "Age",
type: "number"
},
{
name: "gender",
label: "Gender",
type: "radio",
options: [
{
value: "male",
label: "Male"
},
{
value: "female",
label: "Female"
}
]
},
{
name: "country",
label: "Country",
type: "select",
options: [
{
value: "india",
label: "India"
},
{
value: "usa",
label: "USA"
}
]
}
];

React - Encountered two children with the same key

I'm fairly new to React. I am working on a note app and when I add 2 notes, they have the same key and the next 2 notes also share their own key and so on. I started off with prop drilling from the App to the AddNote file via NotesList.js and it was working fine and the problem has only occurred since I used useContext API so maybe I am not coding the useContext in the correct way. The useContext component looks like this:
import { createContext } from "react";
const HandleAddContext = createContext();
export default HandleAddContext;
This is my App.js
import { useState } from "react";
import { v4 as uuid } from "uuid";
import NotesList from "./components/NotesList";
import HandleAddContext from "./components/UseContext/HandleAddContext";
const unique_id = uuid();
const small_id = unique_id.slice(0, 8);
const initialState = [
{
id: small_id,
text: "1st note",
date: "12/10/22022",
},
{
id: small_id,
text: "2nd note",
date: "15/10/22022",
},
{
id: small_id,
text: "3rd note",
date: "16/10/22022",
},
{
id: small_id,
text: "4th note",
date: "30/10/22022",
},
];
export const App = () => {
const [notes, setNote] = useState(initialState);
const addHandleNote = (text) => {
console.log(text);
const date = new Date();
const newNote = {
id: small_id,
text: text,
date: date.toLocaleDateString(),
};
console.log(newNote);
const newNotes = [...notes, newNote];
setNote(newNotes);
};
return (
<HandleAddContext.Provider value={addHandleNote}>
<div className="container">
<NotesList notes={notes} />
</div>
</HandleAddContext.Provider>
);
};
export default App;
This is the component with map notes
import Note from "./Note";
import AddNote from "./AddNote";
const NotesList = ({ notes }) => {
return (
<div className="notes-list">
{notes.map((note) => (
<Note key={note.id} id={note.id} text={note.text} date={note.date} />
))}
<AddNote />
</div>
);
};
export default NotesList;
This is the Note:
import { RiDeleteBin6Line } from "react-icons/ri";
const Note = ({ text, date }) => {
return (
<div className="note">
{/* <div> */}
<p>{text}</p>
{/* </div> */}
<div className="note-footer">
<p className="note-footer-text">{date}</p>
<RiDeleteBin6Line />
</div>
</div>
);
};
export default Note;
This is the AddNote.js component
import { useState } from "react";
import { RiSave2Line } from "react-icons/ri";
const AddNote = ({ handleAddNote }) => {
const [addText, setAddText] = useState("");
const [errorMsg, setErrorMsg] = useState("");
//handle text input
const handleChange = (e) => {
console.log(e.target.value);
setAddText(e.target.value);
};
//handle save
const handleSaveClick = () => {
if (addText.trim().length > 0) {
handleAddNote(addText);
}
};
return (
<div>
<textarea
rows="8"
cols="10"
placeholder="Type here to add a note..."
value={addText}
onChange={handleChange}
/>
<div>
<p>200 characters remaining</p>
<RiSave2Line onClick={handleSaveClick} />
</div>
</div>
);
};
export default AddNote;
The issue is your unique_id and small_id are only being generated once due to your function call syntax.
const unique_id = uuid();
Assigns unique_id the result of uuid(), rather than referencing the function. And therefore small_id is simply slicing the already generated uuid. To fix this your must generate a new uuid every time you create a note. Your can create a function that return a new 'small ID' everytime.
function genSmallID() {
return uuid().slice(0, 8);
}
And now when you create your initial notes use the function:
const initialState = [{
id: genSmallID(),
text: "1st note",
date: "12/10/22022",
}, {
id: genSmallID(),
text: "2nd note",
date: "15/10/22022",
}, {
id: genSmallID(),
text: "3rd note",
date: "16/10/22022",
}, {
id: genSmallID(),
text: "4th note",
date: "30/10/22022",
}];
by setting a variable
const small_id = unique_id.slice(0, 8);
you create a variable and assign it to each element of your initialState array's id.
you should delete small_id and unique_id and do this:
const initialState = [{
id: uuid().slice(0, 8),
text: "1st note",
date: "12/10/22022",
}, {
id: uuid().slice(0, 8),
text: "2nd note",
date: "15/10/22022",
}, {
id: uuid().slice(0, 8),
text: "3rd note",
date: "16/10/22022",
}, {
id: uuid().slice(0, 8),
text: "4th note",
date: "30/10/22022",
}];
In order to have different id (here you have always the same), or if the id isn't relevant for you you can always use the element's position in the array as key with the 2nd parameter of the map function like this:
<div className="notes-list">
{notes.map((note, key) => (
<Note key={key} id={note.id} text={note.text} date={note.date} />
))}
<AddNote />

Select dependendent on each others reactjs

On selecting first dropdown then the second dropdown should show corresponding phonenumber in second dropdownlist and vice versa .how can we achieve it?
Code
import Select from "react-select";
const data = [
{ value: 1, label: "max", phone: "123" },
{ value: 2, label: "sam", phone: "345" },
{ value: 3, label: "denis", phone: "4444" }
];
export default function App(props) {
const [select1, setSelect1] = useState([]);
const [select2, setSelect2] = useState([]);
const filter1=data.filter((x)=>{return x.value===select2.value})
const filter2=data.filter((x)=>{return x.value===select1.value})
return (
<div className="App">
<Select
options={data}
value={select1}
onChange={(e) => {
console.log(e);
setSelect1(e);
setSelect2(e.phone);
}}
/>
<Select
options={data}
value={select2}
onChange={(e) => {
setSelect2(e);
setSelect1(e.value);
}}
/>
</div>
);
}

Tag 'CreateStudent' expects at least '5' arguments, but the JSX factory 'React.createElement' provides at most '2'

I'm making a function to get a student's name, age, and e-mail input and register in the array.
index.tsx
import React, { useState, useRef } from "react";
import Student from "./UserList";
import CreateStudent from "./CreateUser";
function StudentList() {
const [students, setStudents] = useState<any>([
{
id: "st001",
name: "김남준",
age: 28,
email: "rm#gmail.com",
},
]);
const nextId = useRef(2);
const [inputs, setInputs] = useState({
name: "",
age: "",
email: "",
});
const { name, age, email } = inputs;
const onDataChange = (e: { target: { name: any; value: any } }) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value,
});
};
const onCreate = () => {
const student = {
id: "st00" + nextId.current,
name,
age,
email,
};
setStudents([...students, student]);
setInputs({
name: "",
age: "",
email: "",
});
nextId.current += 1;
};
return (
<div>
<CreateStudent
name={name}
age={age}
email={email}
onDataChange={onDataChange}
onCreate={onCreate}
/>
{students.map((student: { id: React.Key | null | undefined }) => (
<Student student={student} key={student.id} />
))}
</div>
);
}
export default StudentList;
./CreateStudent.tsx
import React from "react";
function CreateStudent(
{ name }: { name: any },
{ age }: { age: any },
{ email }: { email: any },
{ onDataChange }: { onDataChange: any },
{ onCreate }: { onCreate: any }
) {
return (
<div>
<input type="text" name="name" onChange={onDataChange} value={name} />
<input type="text" name="age" onChange={onDataChange} value={age} />
<input type="text" name="email" onChange={onDataChange} value={email} />
<button onClick={onCreate}>Add</button>
</div>
);
}
export default CreateStudent;
This is the content of the error that occurred to me.
Tag 'CreateStudent' expects at least '5' arguments, but the JSX factory 'React.createElement' provides at most '2'.
name={name}
age={age}
email={email}
onDataChange={onDataChange}
onCreate={onCreate}
I'm actually using these five arguments.
Why are errors occurring here and how do I resolve them?
The CreateStudent is a react functional component, and in most cases it should just accept only one props argument. In your case, you should destructure the props into 5 keys instead of 5 arguments.
interface CreateStudentProps {
name: any;
age: any;
email: any;
onDataChange: any;
onCreate: any;
}
function CreateStudent({
name,
age,
email,
onDataChange,
onCreate
}: CreateStudentProps) {
...your code here
}

React: Nested Array Form - Input field onChange handler

The form data is set by an the data object. Need help figuring out how to update /handleChange the text inputs
I've tried unique name, those probably won't work because it wont match the "key" in the object.
Any help / input is appreciated!
Data:
export default
{
name: "Restaurants Name",
menu: [
{
category: "Appetizers",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Food 2",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Food 1",
desc: "",
price: "300"
}
]
},
{
category: "Entree",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Entree 1",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Entree 1",
desc: "",
price: "300"
}
]
},
]
}
Code:
import React, { useEffect, useState } from "react";
import "./../App.css"
import MenuData from "../data"
function Edit() {
const [formState, setFormState] = useState(MenuData);
useEffect(() => {
console.log(formState)
}, []);
const handleNameChange = (event) => {
const name = event.target.name;
// console.log(name)
setFormState(prevState => ({
formState: { // object that we want to update
...prevState.formState, // keep all other key-value pairs
[name]: event.target.value, // update the value of specific key
menu: {
...prevState.menu,
items: {
...prevState.menu.items
}
}
}
}))
// setFormState({
// ...formState,
// [name]: event.target.value,
// })
};
const handleChange = (categoryIndex, event) => {
// const values = [...formState]
// values[categoryIndex][event.target.name] = event.target.value;
// setFormState(values);
const name = event.target.name;
// setFormState(prevState => ({
// formState: {
// ...prevState.formState,
// menu: {
// ...prevState.menu,
// items{
// ...prevState.items
// }
// }
// }
// }));
};
return (
<div className="App">
<div>
<input name="nameField" id="nameField" maxLength="300" value={formState.name} onChange={handleNameChange} /> <br />
{formState.menu && formState.menu.map((menuitem, categoryIndex) => {
return (
<div key={categoryIndex}>
<div class="line"></div>
<h2>{menuitem.category}</h2>
<input name={"category-" + categoryIndex} id="CategoryField" maxLength="40" categoryIndex={categoryIndex} onChange={event => handleChange(categoryIndex, event)} value={menuitem.category} />
{
menuitem.items.map((item, index) => {
return(
<div key={index}>
<input name={"title-" + index + categoryIndex} id="titleField" maxLength="40" categoryIndex={categoryIndex} onChange={handleChange} value={item.title} /> <br />
<input name="desc" id="descField" maxLength="200" categoryIndex={categoryIndex} onChange={handleChange} value={item.desc} />
<br /><br />
</div>
)
})
}
</div>
)
}
)
}
</div>
</div>
);
}
export default Edit;
UPDATED
Not able to figure out the onChange function to updated nested items

Resources