Повторный выбор: createSelector работает некорректно

У меня проблема с запоминаемыми селекторами.

Читая документы на https://redux.js.org/usage/deriving-data-selectors, я взял эти фрагменты:

      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)
// 15

Моя проблема в том, что функция secondResult регистрирует результат.

Все это небольшая предпосылка.

Сама моя проблема:

  • Я использую реакцию с @reduxjs / toolkit
  • У меня есть список дел
  • Я создал "todoAdapter" для управления списком как объектами.
  • Я хочу использовать memoized selected для обновления одного «todo» без повторного рендеринга всего списка для оптимизации моего приложения.

но...

Когда я отправляю обновление с помощью «adapter.updateOne», стандартный селектор «SelectAll» каждый раз (я думаю) меняет ref.

Пример

У меня есть селекторы из "ломтика"

      export const {
    selectAll,
    selectIds: selectTodosIDs,
    selectTotal: selectTodosCount,
    selectById: selectTodoById
} = todosSelector;

Если я создам этот селектор

      export const selectIdsCustom = createSelector(
    selectTodosIDs,

    (ids) => {
        console.log('execute output function');
        return ....
    }
)

Все в порядке (state.todos.ids, очевидно, не меняется).

Если я создам этот селектор:

      export const selectTodosCustom = createSelector(
    selectAll,

    (todos) => {
        console.log('execute output function');
        return ....
    }
)

selectTodosCustom запускать «всегда».

Почему ???

With updateOne I am modifing "only" an entity inside "state.todos.entities"

Where am I wrong ??What I did not understand?

My app is just un case study. The complete app is on:https://codesandbox.io/s/practical-hermann-60i7i

I only created the same app in typescript and, when I as my app had this problem:

  • I download the original app form link above (official redux example)
  • npm install
  • npm start

But I have the some problem also in the official example!!!!!

Problem is my env? some library version?

Help me plese!!

2 ответа

Когда я отправляю обновление с помощью «adapter.updateOne», стандартный селектор «SelectAll» каждый раз (я думаю) меняет ref.

Да ты прав. Это правильно. из @reduxjs/toolkit зависит от ids и от государства сущностей.

      {
  ids: ['1', '2'],
  entities: {
    1: {
      id: '1',
      title: 'First',
    },
    2: {
      id: '2',
      title: 'Second',
    },
  },
}

Каждый раз, когда вы отправляете обновление с adapter.updateOne, ссылка на объект изменится. Это нормально, вот как immerjs (используемый под капотом reduxtoolkit) обеспечивает правильную неизменяемость:

      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',
    },
  },
};

Если entities объект остался старым, селектор selectAll вернет мемоизированное значение с неправильным заголовком для первого элемента.

Чтобы оптимизировать повторную визуализацию списка (что на самом деле полезно только для больших списков), вы должны использовать селектор selectIds в родительском компоненте и селекторе selectById в дочерних компонентах.

      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>
  );
}

Большое спасибо Denwakeup.

Если вы поищете приложение по ссылке, вы увидите, что там есть фильтры.

Я не могу использовать selectIds, но получаю отфильтрованные идентификаторы из другого селектора на основе «selectAll» и «selectFilters». Мое желание:

  • Если я добавляю или удаляю элемент ... да, выберите Все изменить исх. Все хорошо
  • но если я изменяю одну сущность, зачем менять ссылки сущностей ?? Я меняю внутреннюю сущность, а не весь объект сущности.

Возможно ли изменение только одного объекта ??

Это похоже на то, что у меня есть:

      const item = {
   name: 'X'
}

item.name = 'Y'

В этом случае элемент не меняет ссылку, а в «сущностях» я меняю одну опору внутри него.

Это не правильно???

Спасибо

Другие вопросы по тегам