Pass props between components using state hook - reactjs

sorry if this question is so simple, I want to pass props between components using state hooks, this is the first component:
import React, { useState } from "react";
import NewExpense from "../components/Expenses/NewExpense/NewExpense";
import Expenses from "../components/Expenses/Expenses";
import "../components/Expenses/NewExpense/ExpenseForm.css";
const DUMMY_EXPENSES = [
{
id: "e1",
title: "Lego Simpsons",
amount: 100.5,
date: new Date(2021, 1, 15),
},
{
id: "e2",
title: "Lego Mindstorms",
amount: 200.5,
date: new Date(2021, 2, 15),
},
{
id: "e3",
title: "Lego Batman",
amount: 300.5,
date: new Date(2021, 3, 15),
},
{
id: "e4",
title: "Lego Star Wars",
amount: 400.5,
date: new Date(2021, 4, 15),
},
];
const VerExpensesV3 = () => {
const [expenses, setExpenses] = useState(DUMMY_EXPENSES);
const [showForm, setShowForm] = useState(false);
const addExpenseHandler = (expense) => {
setExpenses((prevExpenses) => {
return [expense, ...prevExpenses];
});
};
const statusFormHandler = () => {
return showForm;
}
const showFormHandler = () => {
setShowForm(true);
};
return (
<div>
<h1>My expenses</h1>
{showForm && (
<NewExpense
onAddExpense={addExpenseHandler}
statusForm={statusFormHandler}
/>
)}
<div className="new-expense__actions">
<button onClick={showFormHandler}>Add New Expense</button>
</div>
<Expenses records={expenses} />
</div>
);
};
export default VerExpensesV3;
this is the second one:
import React, { useState } from 'react';
import './NewExpense.css';
import ExpenseFormV2 from './ExpenseFormV2';
const NewExpense = (props) => {
const [statusForm, setStatusForm] = useState(props.statusFormHandler);
console.log("el valor de statusForm en NewExpense es: "+statusForm);
const saveExpenseDataHandler =
(enteredExpenseData) => {
const expenseData = {
...enteredExpenseData,
id: Math.random().toString()
};
props.onAddExpense(expenseData);
};
return (
<div className="new-expense">
<ExpenseFormV2
onSaveExpenseData={saveExpenseDataHandler}
currentStatusForm={statusForm}/>
</div>
)
};
export default NewExpense;
So far I'm stuck because in these lines:
const [statusForm, setStatusForm] = useState(props.statusFormHandler);
console.log("el valor de statusForm en NewExpense es: "+statusForm);
I get "undefined", from my point of view the value passed is NULL from component 1 to component 2 and I don't understand why, so your comments and suggestions will be appreciated.
Thanks a lot

You're using the wrong property on props in <NewExpense>. Try this instead:
const [statusForm, setStatusForm] = useState(props.statusForm);
statusForm is the prop you defined on the component, and statusFormHandler is the name of the function you assign to the statusForm in the first component.
If you actually want your prop name to be statusFormHandler then disregard the above and use this
<NewExpense
onAddExpense={addExpenseHandler}
statusFormHandler={statusFormHandler}
/>

In my case I was using lower letter variable name.
const newExpense = (props) => {
const [isDefault, setDefault] = useState(true);
const saveNewExpenseHandler = (expenseData) => {
props.onSaveExpense(expenseData);
};
return (
<div className="new-expense">
<ExpenseForm onSavePressed={saveNewExpenseHandler} />
</div>
);
};
export default newExpense;
once I changed newExpense to NewExpense it all worked fine

Related

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 />

React Datepicker for rendering list is always one day behind

I am building a ToDoList with React and a Django rest Api but I am also using a Datepicker to render all the tasks for the day by the date created. So if I choose the 12.06 the api calls all the tasks for that day and displays them. But every time I change the date to show the list for the day the list for that day is not shown.
Only if i change it to another day the list of the previous date is shown. For example I choose the 12.06 nothing appears but then switch to the 11.06 the tasks for the 12th are being rendered. If I console log the dates in the Datepicker it shows the right day but if I do it inside the handleDateChange I hangs behind.
import 'date-fns'
import Grid from '#material-ui/core/Grid'
import DateFnsUtils from '#date-io/date-fns'
import{
MuiPickersUtilsProvider,
KeyboardTimePicker,
KeyboardDatePicker
} from '#material-ui/pickers'
import TodoForm from '../ToDo/TodoForm'
function Datepicker() {
const initialDate = new Date(Date.now())
const [selectDate, setSelectDate] = useState(
`${initialDate.getFullYear()}-${initialDate.getMonth()+1}-${initialDate.getDate()}`
)
console.log("In App selected Date = ", selectDate)
const handleDateChange = (date) =>{
// setSelectDate(date)
setSelectDate(`${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`)
console.log("TimePicker selected Date = ", selectDate)
}
return (
<div>
<div>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<Grid container justify='space-around'>
<KeyboardDatePicker
disableToolbar
varient='inline'
format='MM/dd/yy'
margin='normal'
id='date-picker'
label='Pick your Date'
value={selectDate}
onChange={handleDateChange}
KeyboradButtonProps={{
'aris-label': 'change date'
}}
/>
</Grid>
</MuiPickersUtilsProvider>
</div>
<div>
<TodoForm date={selectDate}/>
</div>
</div>
)
}
export default Datepicker
import React, { Component, useState,useEffect } from 'react'
import Select from 'react-select'
import { apiTaskCreate } from './lookup'
import { ActionBtn } from './buttons'
import TodoList from './TodoList'
function TodoForm(props) {
const [newTasks, setNewTasks] = useState([])
const [taskname, SetTaskname] = useState('')
const [Importants, setImportants] = useState({})
const [TimeComplete, setTimeComplete] = useState({})
const date_of_task = props.date
const handleChange = e => {
SetTaskname(e.target.value)
}
function onChangeImportants(value){
setImportants(value.value)
}
function onChangeTimeComplete(value){
setTimeComplete(value.value)
}
const handleSubmit = e =>{
e.preventDefault()
SetTaskname('')
let tempNewTasks = [...newTasks]
apiTaskCreate(taskname,Importants,TimeComplete,date_of_task,(response, status)=>{
// console.log(response, status)
if (status === 201){
tempNewTasks.unshift(response)
setNewTasks(tempNewTasks)
} else {
console.log(response)
alert("an error accourd")
}
})
}
const Importants_options = [
{ value: '1', label: 1 },
{ value: '2', label: 2 },
{ value: '3', label: 3 },
{ value: '4', label: 4 },
{ value: '5', label: 5 },
]
const Time_options = [
{ value: '1', label: 30 },
{ value: '2', label: 60 },
{ value: '3', label: 90 },
{ value: '4', label: 120 },
{ value: '5', label: 150 },
{ value: '6', label: 180 },
]
return (
<div className={props.className}>
<div className='col-11 mb-3'>
<form className='todo-form mb-3' onSubmit={handleSubmit}>
<input type='text' value={taskname} placeholder='Task Name'
name='task_name' className='todo-input' onChange={handleChange}></input>
<Select onChange={onChangeImportants} options={Importants_options} placeholder="Importants Score"/>
<Select onChange={onChangeTimeComplete} options={Time_options} placeholder="Time to complete"/>
<button className='btn btn-primary'>Submit</button>
<ActionBtn action={{type: 'optimize', display:"Optimize"}}/>
</form>
</div>
<div className='container'>
<TodoList newTasks={newTasks} {...props}/>
</div>
</div>
)
}
export default TodoForm
import React, {useState, useEffect} from 'react'
import { apiTaskList } from './lookup'
import Task from './Task'
function TodoList(props) {
const [tasksInit, setTasksInit] = useState([])
const [tasks, setTasks] = useState([])
const [tasksDidSet, setTasksDidSet] = useState(false)
// const initialDate = new Date(Date.now())
function join(t, a, s) {
function format(m) {
let f = new Intl.DateTimeFormat('en', m);
return f.format(t);
}
return a.map(format).join(s);
}
let a = [{year: 'numeric'},{month: 'numeric'},{day: 'numeric'}];
const initialDate = join(new Date, a, '-');
const [date, setDate] = useState(initialDate)
// `${initialDate.getFullYear()}-${initialDate.getMonth()+1}-${initialDate.getDate()}`
useEffect( () =>{
const final = [...props.newTasks].concat(tasksInit);
setTasks(tasks => {
if (final.length !== tasks.length) {
return final;
}
return tasks
});
}, [props.newTasks, tasksInit])
useEffect(() => {
if (tasksDidSet === false) {
const handleTasksListLookup = (response, status) => {
if (status === 200) {
setTasksInit(response);
setDate(props.date);
}
}
apiTaskList("admin", date ,handleTasksListLookup)
}
}, [setTasksInit, props.date, tasksDidSet])
return tasks.map((item, index)=>{
return <Task task={item} className='d-flex p-2 justify-content-between border bg-white text-dark' key={`${index}-${item.id}`}/>
})
}
export default TodoList;
import { backendlookup } from "../lookup/lookup";
export function apiTaskCreate(newTask_Name,newImportans_Score,newTime_to_complete,new_date_of_task,callback) {
backendlookup('POST', 'create',callback, {
Task_name: newTask_Name,
Importants_Score: newImportans_Score,
Time_to_Finish: newTime_to_complete,
date_of_task: new_date_of_task,
})
}
export function apiTaskList(username,date,callback) {
let endpoint = 'tasks'
if (date){
endpoint = `tasks?username=${username}&date=${date}`
}
backendlookup('GET', endpoint ,callback)
}
export function apiPartyActionOptimize(action,callback) {
backendlookup('POST', 'action-optimize',callback, {action:action})

Default value is not selected with react-select (4.3.1) using React Hooks

I am trying to auto-selected a value from a list of data in the selected component. Kindly help.
have already tried with the isLoading flag as well.
If the selected value is available in the list then auto selection
if value not available then no issue.
import React, {useEffect, useState} from 'react';
import Select from 'react-select';
import './App.css';
function App() {
const [selectCity, setSelectCity] = useState(null);
const [cityOptions, setCityOptions] = useState([]);
useEffect(() => {
setSelectCity("Mumbai");
setCityOptions([{label: "Kolkata", value:"Kolkata"}, {label: "New Delhi", value:"New Delhi"}, {label: "Chennai", value:"Chennai"}, {label: "Mumbai", value:"Mumbai"}])
}, []);
const onCitySelect = (e) => {
console.log("Selected: ", e);
};
return (
<div className="App">
<Select
defaultValue={selectCity}
options={cityOptions}
onChange={onCitySelect}
/>
</div>
);
}
export default App;
Please pass object "setSelectCity({ label: "Kolkata", value: "Kolkata" });"
import './App.css';
import React, { useEffect, useState } from 'react';
import Select from 'react-select';
const App = () => {
const [selectCity, setSelectCity] = useState(null);
const [cityOptions, setCityOptions] = useState([]);
useEffect(() => {
setSelectCity({ label: "Kolkata", value: "Kolkata" });
setCityOptions([{ label: "Kolkata", value: "Kolkata" }, { label: "New Delhi", value: "New Delhi" }, { label: "Chennai", value: "Chennai" }, { label: "Mumbai", value: "Mumbai" }])
}, []);
const onCitySelect = (e) => {
console.log("Selected: ", e);
setSelectCity(e);
};
return (
<div className="App">
<h1>Hello MERN !!</h1>
<Select
value={selectCity}
options={cityOptions}
onChange={onCitySelect}
/>
</div>
);
}
export default App;
Try using the key prop
<Select
defaultValue={selectCity}
options={cityOptions}
onChange={onCitySelect}
key={selectCity}
/>
The defaultValue props cannot be set dynamically, so you have to set it manually. And please note that the defaultValue should be an object containing the label and the actual value.
import ReactDOM from "react-dom";
import React, { useEffect, useState } from "react";
import Select from "react-select";
function SelectMod() {
const [selectCity, setSelectCity] = useState(null);
const [cityOptions, setCityOptions] = useState([]);
useEffect(() => {
setCityOptions([
{ label: "Kolkata", value: "Kolkata" },
{ label: "New Delhi", value: "New Delhi" },
{ label: "Chennai", value: "Chennai" },
{ label: "Mumbai", value: "Mumbai" }
]);
}, []);
const onCitySelect = (e) => {
console.log("Selected: ", e);
};
return (
<div className="App">
<Select
defaultValue={{ label: "Mumbai", value: "Mumbai" }}
options={cityOptions}
onChange={onCitySelect}
/>
</div>
);
}
export default SelectMod;
const rootElement = document.getElementById("root");
ReactDOM.render(<SelectMod />, rootElement);
Or just set it as the default state value.
const [selectCity, setSelectCity] = useState({ label: "Mumbai", value: "Mumbai" });
--snips--
defaultValue={selectCity}

Rect - change the state of a property of an array mapper onclick (in functional no class)

I JSON data receive in function params , the data are mapped and display (media table). Each media contains a number of likes (ex: 88). Each time a user clicks on the like it must increase by 1 the value.
Do you have an idea, I would like to do it functionally (no class), Thx.
import React, { useState } from "react";
import _ from 'lodash';
//Data receive in params
const medias = [{
"id": 623534343,
"like": 88,
"date": "2019-02-03"
}, {
"id": 625025343,
"like": 85,
"date": "2019-04-03"
}, {
"id": 2525345343,
"like": 34,
"date": "2019-04-05"
}];
const Test = ({ medias }) => {
//const [items, setItems] = useState([]);
const handleLike = (id) => {
//???
}
return (
<>
{medias.map((media, index) => {
return (
<>
<div key={`galerie-${index}`}>
<p>{media.like}</p>
<button onClick={() => handleLike(media.id)}></button>
</div>
</>
)
})}
</>
);
}
export default Test;
I found the solution to add a system of like incrementation.
Maybe it can help someone
https://codesandbox.io/s/pedantic-fire-mbw7i?file=/src/App.js
import React, { useState, useEffect } from "react";
import _ from "lodash";
const Test = ({ id }) => {
const [medias, setMedias] = useState([]);
useEffect(() => {
fetchMedias();
}, []);
//Get the medias by ID from API
const fetchMedias = async () => {
//const response = await fetch(`api/.../${id}/medias`);
//const data = await response.json();
//Data normally received from API
let mediaJsonExemple = [
{ id: 342550, like: 62, date: "2019-02-07" },
{ id: 8520927, like: 11, date: "2019-03-03" },
{ id: 9025895, like: 72, date: "2019-04-03" }
];
setMedias(_.orderBy(mediaJsonExemple, "date"));
};
const handleLike = (id) => {
const newMedias = [...medias];
const mediasUntouched = newMedias.filter((m) => m.id !== id);
const mediaToUpdate = _.find(newMedias, { id });
mediaToUpdate.like++;
mediasUntouched.push(mediaToUpdate);
const sortedMedia = _.orderBy(mediasUntouched, "date");
setMedias(sortedMedia);
};
let totalLikes = _.sumBy(medias, "like");
return (
<>
<p>Total likes = {totalLikes}</p>
{medias.map((media, index) => {
return (
<>
<div>
<p>{media.like}</p>
<button onClick={() => handleLike(media.id)}>ADD</button>
</div>
</>
);
})}
</>
);
};
export default Test;

Why component is not re-rendering after deletion in react

Trying to delete element from list but its not re-rendering even I am using useEffect. my code is
import React from "react";
import "./styles.css";
import { useEffect, useState } from "react";
const initialList = [
{
id: 'a',
firstname: 'Robin',
lastname: 'Wieruch',
year: 1988,
},
{
id: 'b',
firstname: 'Dave',
lastname: 'Davidds',
year: 1990,
},
{
id: 'c',
firstname: 'ssh',
lastname: 'asssss',
year: 1990,
},
{
id: 'd',
firstname: 'Asdf',
lastname: 'we32e',
year: 1990,
},
];
export default function App() {
const [list, setList] = useState(initialList);
useEffect(() => {
console.log('useEffect has been called!');
setList(list);
}, [list]);
const handleRemove = (id,i) => {
list.splice(i,1)
setList(list);
}
return (
<div className="App">
<ul>
{list.map((item,i) => (
<li key={item.id}>
<span>{item.firstname}</span>
<span>{item.lastname}</span>
<span>{item.year}</span>
<button type="button" onClick={() => handleRemove(item.id,i)}>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
It's always a problem in react modifying the state directly and is generally considered an anti-pattern.
You could just do something like:
const handleRemove = (id) => {
const newArr = list.filter((el) => el.id !== id);
setList(newArr);
}
And you don't need any useEffect either, the function should handle the state change.

Resources