To be more specific, when displaying a list of the same components containing textarea, how to set the initial state of the last component textarea to a specific value?
For example, I have a simple Note app.
It basically just adds, updates and deletes notes.
To make it simple I just use one Note component:
import { useState } from "react";
import { v4 as uuid } from "uuid";
import { MdDeleteForever } from "react-icons/md";
export const Note = ({ info, data, setData, isNewNote }) => {
const [text, setText] = useState(info.text);
console.log("text = ", text, "id = ", info.id);
const onChange = (event) => {
event.preventDefault();
const value = event.currentTarget.value;
setText(value);
};
const onSave = () => {
const newNote = {
id: uuid(),
text: text
};
setData((prev) => [...prev, newNote]);
// Need to reset the text for the unsaved new note
setText("Try to add a new note.");
};
const onDelete = (event) => {
const id = event.currentTarget.getAttribute("data-value");
setData((prev) => data.filter((item) => item.id !== id));
};
return (
<div
className={`m-3 w-56 h-36 ${isNewNote ? "bg-teal-200" : "bg-yellow-300"}`}
>
<textarea
className={`overflow-hidden p-2 bg-transparent focus:outline-0 resize-none border-transparent ${
isNewNote ? "bg-transparent" : "bg-yellow-300"
}`}
name=""
id=""
cols="23"
rows="3"
value={text}
onChange={onChange}
></textarea>
<div className="flex items-center justify-end m-3">
{isNewNote ? (
<button
className="w-20 h-7 drop-shadow-md bg-blue-500 text-slate-100 rounded-md hover:bg-blue-700 transition duration-300"
onClick={onSave}
>
Save
</button>
) : (
<MdDeleteForever
className="text-2xl drop-shadow-md hover:cursor-pointer hover:scale-125 transition duration-500"
onClick={onDelete}
data-value={info.id}
/>
)}
</div>
</div>
);
};
App.js:
import { useState } from "react";
import { Note } from "./components";
import { v4 as uuid } from "uuid";
export default function App() {
const [data, setData] = useState([
{
id: uuid(),
text: "This is my first note."
},
{
id: uuid(),
text: "This is my second note."
},
{
id: uuid(),
text: "This is my third note."
}
]);
return (
<div className="mt-3 flex flex-col items-center justify-center">
<div className="font-bold text-3xl">Note app</div>
<div className="flex items-center justify-center flex-wrap">
{data &&
data.map((note, index) => {
return (
<div key={index}>
<Note info={note} data={data} setData={setData} />
</div>
);
})}
<div key="x1">
<Note
info={{ text: "Try to add a new note." }}
data={data}
setData={setData}
isNewNote={true}
/>
</div>
</div>
</div>
);
}
I pass some "placeholder" text as props to the Note component hoping the component will show the text when the note isn't saved to the list.
In the textarea, the text is decided by the onChange method but first-time display I sent the props to the useState() at the beginning of the component so the text would be what is stored in the data array or whatever "placeholder" text I put in the props.
Every time it displays the last Note component, it is reusing the text from the previous saved Note component, UNLESS I specifically add a setText() at the end of the onSave function to reset the text state to the "placeholder" value.
Why doesn't the last Note component update the text state with the incoming props? I have tried numerous ways to update the text with the "placeholder" text thinking that I could update the text when the last Note component is created but I always failed.
Am I missing the big picture here? How does a component keep its state when we render multiple components with the same code?
Example is here:
https://codesandbox.io/s/note-app-kpxolc?file=/src/components/Note.js