I have a small React app where you can click on a piece of text to replace it with an input field, to allow you to edit it.
The problem is that the EditableText component does not seem to want to display the editingText state in the input field.
See example here: https://codesandbox.io/s/distracted-ellis-kyrdb
For posterity, the code for the main app is:
export default function App() {
const [cats, setCats] = useState([
{
id: 1,
name: "fred"
},
{
id: 2,
name: "jim"
}
]);
const [activeCat, setActiveCat] = useState({});
const handleClick = (cat, e) => {
setActiveCat(cat);
};
const saveName = (newName) => {
// save this cat's new name...
};
return (
<div className="App">
<div className="catMenu">
{cats.map((row) => (
<div
className="catItem"
key={row.id}
onClick={(e) => handleClick(row, e)}
>
{row.name}
</div>
))}
</div>
<div className="activeCat">
<h2>
<EditableText text={activeCat.name} saveText={saveName} />
</h2>
</div>
</div>
);
}
and for the EditableText component:
const EditableText = (props) => {
const { text, saveText } = props;
const [editing, setEditing] = useState(false);
const [editingText, setEditingText] = useState(text);
useEffect(() => {
const handleKeydown = (e) => {
if (editing) {
if (e.keyCode === 13) {
// Enter key pressed
saveText(editingText);
setEditing(false);
}
if (e.keyCode === 27) {
// Escape key pressed
setEditingText(text);
setEditing(false);
}
}
};
window.addEventListener("keydown", handleKeydown);
return () => {
window.removeEventListener("keydown", handleKeydown);
};
}, [text, saveText, editing, editingText]);
return (
<div>
<div
onClick={() => setEditing(true)}
className={`et-${editing ? "hidden" : "active"}`}
>
{text}
</div>
<input
type="text"
value={editingText}
onChange={(e) => setEditingText(e.target.value)}
className={`et-input-${editing ? "active" : "hidden"}`}
/>
</div>
);
};
export default EditableText;
Add another useEffect to listen to text prop change, you only use it as initial empty value:
useEffect(() => {
setEditingText(text);
}, [text]);