Tengo un componente controlado que llamo Note. Quiero que su valor predeterminado sea igual a la nota seleccionada (que se establece en App.js y se pasa como accesorio). Parece una práctica redundante/mala. Aquí está mi código, simplificado a las partes relevantes. ¿Cómo puedo configurar el valor predeterminado de textarea para que sea igual a otra variable de estado?
Editar: Olvidé mencionar que la nota seleccionada se cambia en otro componente. Funciona para el estado establecido en useEffect pero no para las actualizaciones.
Aplicación.js
function App(){ const [selectedNote, setSelectedNote] = useState("") useEffect(() => { async function fetchData(){ let req = await fetch("http://localhost:9292/notes"); let res = await req.json(); setSelectedNote(res[0]) } fetchData() },[]) return ( <Note selectedNote={selectedNote.body}/> ) }nota.js
function Note({selectedNote}) { const [editValue, setEditValue] = useState(selectedNote) return ( <form> <textarea value={editValue} onChange={handleChange}> </textarea> </form> ) } (Para aclarar, no tengo problemas si escribo const [editValue, setEditValue] = useState("testing123") o alguna otra cadena)
Por lo tanto, lo ideal es elevar el estado para que el componente principal administre las actualizaciones de estado y el componente de Notes sea lo más tonto posible.
En este ejemplo, los datos se cargan en el estado y luego se crean las notas, solo recibiendo una identificación, un texto del cuerpo que será su valor y un controlador onChange .
Cuando se cambia el texto, se copia el estado, se actualiza el objeto en la matriz (definido por el id) y la nueva matriz vuelve al estado.
const { useEffect, useState } = React; const json = '[{"id":1,"body":"Note1"},{"id":2,"body":"Note2"},{"id":3,"body":"Note3"}]'; function mockApi() { return new Promise(res => { setTimeout(() => res(json), 2000); }); } function Example() { const [ notes, setNotes ] = useState([]); const [ selectedNote, setSelectedNote ] = useState(null); useEffect(() => { mockApi() .then(res => JSON.parse(res)) .then(data => setNotes(data)); }, []); function handleChange(e) { const { value, dataset: { id } } = e.target; const copy = [...notes]; copy[id - 1].body = value; setNotes(copy); } function handleClick() { console.log(JSON.stringify(notes)); } if (!notes.length) return 'Loading'; return ( <div> {notes.map(note => { return ( <Note key={note.id} id={note.id} body={note.body} handleChange={handleChange} /> ) })} <button onClick={handleClick}> View state </button> </div> ); } function Note({ id, body, handleChange }) { return ( <textarea data-id={id} value={body} onChange={handleChange} /> ); } ReactDOM.render( <Example />, document.getElementById('react') ); <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script> <div id="react"></div>Puede proporcionar una función a useState que solo se invocará una vez, cuando se represente el componente. Use esa función para copiar el valor prop en el estado privado de la Nota.
const [editValue, setEditValue] = useState(() => selectedNote)Es posible que tenga otros problemas, como que el valor de la propiedad esté en blanco en el renderizado inicial, pero esta suele ser la forma más sencilla de inicializar una variable de estado privada basada en una propiedad.
Si resulta que el estado inicial en blanco es un problema insuperable, es posible que deba configurar un useEffect que actualice el estado privado cuando el valor prop cambie a un valor satisfactorio. Algo como esto:
const [editValue, setEditValue] = useState() React.useEffect(() => { // only update state if old is blank & new is not if(!editValue && selectedNote) setEditValue(selectedNote) }, [selectedNote])