¿Método push en React Hooks (useState)?

Respuestas:

264

Cuando lo usa useState, puede obtener un método de actualización para el elemento de estado:

const [theArray, setTheArray] = useState(initialArray);

luego, cuando desee agregar un nuevo elemento, use esa función y pase la nueva matriz o una función que creará la nueva matriz. Normalmente esto último, dado que las actualizaciones de estado son asincrónicas y, a veces, por lotes:

setTheArray(oldArray => [...oldArray, newElement]);

A veces puede salirse sin usar ese formulario de devolución de llamada, si solo actualiza la matriz en los controladores para ciertos eventos de usuario específicos como click(pero no como mousemove):

setTheArray([...theArray, newElement]);

Los eventos para los que React asegura que el renderizado se vacíe son los "eventos discretos" enumerados aquí .

Ejemplo en vivo (pasando una devolución de llamada a setTheArray):

Debido a que la única actualización que theArrayhay es la de un clickevento (uno de los eventos "discretos"), podría salirme con una actualización directa en addEntry:

TJ Crowder
fuente
1
... e intente setTheArray(currentArray => [...currentArray, newElement])si lo anterior no funciona. Lo más probable es en useEffect(...theArray..., []), es importante darse cuenta de que theArrayes una constante, por lo que las actualizaciones funcionales son necesarios para los valores de futuro renders
Aprillion
@Aprillion: ¿No estás seguro de lo que estás diciendo con ese useEffectejemplo ...?
TJ Crowder
1
Si useEffecttiene una lista de dependencias vacía [], se ejecutará solo durante el primer renderizado. Entonces el valor del theArrayinterior del efecto siempre será initialArray. En situaciones en las setTheArray([...initialArray, newElement])que no tendría sentido, y cuando la theArrayconstante siempre será igual a initialValue, se necesitan actualizaciones funcionales.
Aprillion
@Aprillion - Me temo que todavía no lo entiendo, posiblemente sea un poco denso. :-) ¿Por qué necesitarías un efecto con solo el valor de la matriz inicial ? (Quiero decir, puede haber un caso de uso poco frecuente, pero ...) E incluso si lo hace, ¿qué tiene que ver con la pregunta?
TJ Crowder
Hola @TJCrowder, utilizo la forma de devolución de llamada para fusionar datos antiguos con nuevos cuando obtengo datos, pero por alguna razón no funciona correctamente, ¿puedes marcar esto para comprender mi problema?
Oliver D
45

Para expandir un poco más, aquí hay algunos ejemplos comunes. Empezando con:

const [theArray, setTheArray] = useState(initialArray);
const [theObject, setTheObject] = useState(initialObject);

Empuje el elemento al final de la matriz

setTheArray(prevArray => [...prevArray, newValue])

Insertar / actualizar elemento al final del objeto

setTheObject(prevState => ({ ...prevState, currentOrNewKey: newValue}));

Insertar / actualizar elemento al final de la matriz de objetos

setTheArray(prevState => [...prevState, {currentOrNewKey: newValue}]);

Empuje el elemento al final del objeto de las matrices

let specificArrayInObject = theObject.array.slice();
specificArrayInObject.push(newValue);
const newObj = { ...theObject, [event.target.name]: specificArrayInObject };
theObject(newObj);

Aquí también hay algunos ejemplos prácticos. https://codesandbox.io/s/reacthooks-push-r991u

Elia Ahadi
fuente
4
Gracias por todos los ejemplos. Exactamente con lo que estaba teniendo un problema era con su elemento Push al final del objeto de las matrices . Probé tantas cosas diferentes, pero no pude improvisarlas.
sdouble
5

De la misma manera que lo haces con el estado "normal" en los componentes de la clase React.

ejemplo:

function App() {
  const [state, setState] = useState([]);

  return (
    <div>
      <p>You clicked {state.join(" and ")}</p>
      //destructuring
      <button onClick={() => setState([...state, "again"])}>Click me</button>
      //old way
      <button onClick={() => setState(state.concat("again"))}>Click me</button>
    </div>
  );
}
rg
fuente
esto funcionará con onClick, pero el setState (oldState => [... oldState, pushsomething]) es cómo debe hacerlo
Cyrus Zei
3
// Save search term state to React Hooks with spread operator and wrapper function

// Using .concat(), no wrapper function (not recommended)
setSearches(searches.concat(query))

// Using .concat(), wrapper function (recommended)
setSearches(searches => searches.concat(query))

// Spread operator, no wrapper function (not recommended)
setSearches([...searches, query])

// Spread operator, wrapper function (recommended)
setSearches(searches => [...searches, query])

https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc

Adarsh ​​Pawar
fuente
2

setTheArray([...theArray, newElement]);es la respuesta más simple, pero tenga cuidado con la mutación de elementos en el Array . Utilice la clonación profunda de elementos de matriz.

Shivang Gupta
fuente
La clonación profunda que mencionas, ¿significa recibir como objA? UseState y concat el objA con objB? ¿Igual que este enlace? gist.githubusercontent.com/kahsing/…
Luiey
1

El método más recomendado es usar la función de envoltura y el operador de extensión juntos. Por ejemplo, si ha inicializado un estado llamado nameasí,

const [names, setNames] = useState([])

Puede empujar a esta matriz de esta manera,

setNames(names => [...names, newName])

Espero que ayude.

Khondoker Zahidul Hossain
fuente
No publique respuestas que solo incluyan enlaces. El vínculo podría romperse en el futuro. En su lugar, cree una reseña de ese artículo y asegúrese de responder la pregunta.
BlackCetha
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada. - De la revisión
ilke444
0

Probé los métodos anteriores para insertar un objeto en una matriz de objetos en useState pero tuve el siguiente error al usar TypeScript :

Escriba 'TxBacklog [] | undefined 'debe tener un método' Symbol.iterator 'que devuelva un iterator.ts (2488)

La configuración de tsconfig.json aparentemente era correcta:

{
   "compilerOptions": {
   "target": "es6",
   "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es6",
],

Esta solución resolvió el problema (mi código de muestra):

Interfaz:

   interface TxBacklog {
      status: string,
      txHash: string,
   }

Variable de estado:

    const [txBacklog, setTxBacklog] = React.useState<TxBacklog[]>();

Empuje un nuevo objeto en la matriz:

    // Define new object to be added
    const newTx = {
       txHash: tx,
       status: 'pending'
    };
    // Push new object into array
    (txBacklog) 
       ? setTxBacklog([...txBacklog, newTx])
       : setTxBacklog([newTx]);

Leí otras alternativas como agregar '!' al nuevo objeto, pero no funcionó tan bien.

Sergi Juanati
fuente