Tengo un problema con mis selectores memorizados.
Al leer los documentos en https://redux.js.org/usage/deriving-data-selectors , tomé estos fragmentos:
const state = { a: { first: 5 }, b: 10 } const selectA = state => state.a const selectB = state => state.b const selectA1 = createSelector([selectA], a => a.first) const selectResult = createSelector([selectA1, selectB], (a1, b) => { console.log('Output selector running') return a1 + b }) const result = selectResult(state) // Log: "Output selector running" console.log(result) // 15 const secondResult = selectResult(state) // No log output console.log(secondResult) // 15Mi problema es que la función secondResult registra el resultado.
Todo esto es una pequeña premisa.
Mi mismo problema:
pero...
Cuando envío una actualización con "adapter.updateOne", el selector estándar "SelectAll" cada vez (creo) cambia ref.
Ejemplo
Tengo estos selectores de "rebanada"
export const { selectAll, selectIds: selectTodosIDs, selectTotal: selectTodosCount, selectById: selectTodoById } = todosSelector;Si creo este selector
export const selectIdsCustom = createSelector( selectTodosIDs, (ids) => { console.log('execute output function'); return .... } )Todo está bien (state.todos.ids no cambia obviamente).
Si creo este selector:
export const selectTodosCustom = createSelector( selectAll, (todos) => { console.log('execute output function'); return .... } )selectTodosCustom ejecutar "siempre".
¿Por qué?
Con updateOne estoy modificando "solo" una entidad dentro de "state.todos.entities"
¿Dónde estoy equivocado? ¿Lo que no entendí?
Mi aplicación es solo un caso de estudio. La aplicación completa está en: https://codesandbox.io/s/practical-hermann-60i7i
Solo creé la misma aplicación en mecanografiado y, cuando yo como mi aplicación tuve este problema:
¡Pero también tengo algún problema en el ejemplo oficial!
¿El problema es mi env? alguna versión de la biblioteca?
Ayúdame por favor!!
Cuando envío una actualización con "adapter.updateOne", el selector estándar "SelectAll" cada vez (creo) cambia ref.
Sí, tiene usted razón. Es correcto. selectAll de @reduxjs/toolkit depende de los ids y las entities del estado de las entidades.
{ ids: ['1', '2'], entities: { 1: { id: '1', title: 'First', }, 2: { id: '2', title: 'Second', }, }, } Cada vez que envía una actualización con adapter.updateOne , la referencia al objeto de las entities cambia. Esto es normal, así es como immerjs (usado bajo el capó de reduxtoolkit) proporciona la inmutabilidad correcta:
dispatch(updateOne({ id: '1', changes: { title: 'First (altered)' } })); const state = { ids: ['1', '2'], entities: { // <--- new object 1: { // <--- new object id: '1', title: 'First (altered)', }, 2: { // <--- old object id: '2', title: 'Second', }, }, }; Si el objeto de las entities siguiera siendo antiguo, el selector selectAll devolvería un valor memorizado con un título incorrecto para el primer elemento.
Para optimizar la nueva representación de la lista (que en realidad solo es útil para listas grandes), debe usar el selector selectIds en el componente principal y el selector selectById en los componentes secundarios.
const Child = ({ id }) => { const dispatch = useDispatch(); const book = useSelector((state) => selectById(state, id)); const handleChange = useCallback(() => { // the parent component will not be re-render dispatch( bookUpdate({ id, changes: { title: `${book.title} (altered)`, }, }) ); }, [dispatch, id, book]); return ( <div> <h2>{book.title}</h2> <button onClick={handleChange}>change title</button> </div> ); }; function Parent() { const ids = useSelector(selectIds); return ( <div> {ids.map((id) => ( <Child key={id} id={id}></Child> ))} </div> ); }ACTUALIZAR
En este caso, el artículo no cambia la referencia y en "entidades" estoy cambiando un solo accesorio dentro de él.
¿¿¿No está bien???
No, no puedes hacer eso cuando usas redux. Redux se basa en la idea de inmutabilidad. Cualquier cambio de estado crea un nuevo objeto de estado. Actualizar una entidad es solo un cambio profundo del objeto de estado. Y todos los objetos en la ruta al elemento actualizado deben ser nuevos. Si no sigue esta regla, las herramientas básicas no funcionarán correctamente. Todos ellos usan una comparación estricta por defecto.
Pero aún puede evitar volver a renderizar el componente incluso cuando el selector devuelve matrices iguales con diferentes referencias. Simplemente pase su propia función de comparación:
... const todoIds = useSelector( selectFilteredTodoIds, // the component will be re-render only if this function returns false (next, prev) => next.every((id, index) => id === prev[index]) ); ...Muchas gracias Denwakeup.
Si buscas la aplicación en el enlace, verás que hay filtros.
No puedo usar selectIds pero obtengo las identificaciones filtradas de otro selector basado en "selectAll" y "selectFilters". Mi deseo es:
¿Es posible cambiar solo una sola entidad?
Es como si tuviera:
const item = { name: 'X' } item.name = 'Y'En este caso, el elemento no cambia la referencia y en "entidades" estoy cambiando un solo accesorio dentro de él.
¿¿¿No está bien???
Gracias