React Redux получает доступ к динамически отфильтрованному состоянию в mapStateToProps - проблемы с перерисовкой

У меня есть функциональный компонент, которому передаются инструкции о том, что вытащить из хранилища redux. Используя mapStateToProps=(state, ownProps), я могу с радостью извлекать необходимые элементы из состояния (хранилища) - но ценой любых изменений во всем дереве состояний, вызывающих повторный запуск mapStateToProps и множество повторных рендеров.

Дай мне распаковать.

Вот снимок части магазина:

{
  settings: {...stuff...},
  projects: [...stuff...],
  definitions: [...stuff...],
  themes: [...stuff...],
  surfaces: {
    '6': {                                   <--- VARIABLE PASSED TO COMPONENT
      surface: {
        STRIP: [..stuff..],
        GLOBAL: {                            <--- CATEGORY PASSED TO COMPONENT
          DISPLAY: {...stuff...},
          ASSIGNMENT: {                      <--- LIST OF REQUIRED OBJECTS HAS 
            A_TRACK: {                       SUBCATEGORY AND TARGET (A_TRACK etc...)
              value: 0,
              type: 'switch',
              label: 'TRACK'                 
            },
            A_SEND: {                        <--- ANOTHER OBJECT I NEED TO GET
              value: 0,
              type: 'switch',
              label: 'SEND'
            },
            A_PAN: {
              value: 0,
              type: 'switch',
              label: 'PAN'
            },
          },
        FADER_BANKS: {...stuff...},  
        STATUS: {...stuff...},
        LOTS_MORE_STUFF

Мой родительский компонент передает необходимые инструкции дочернему элементу.

<RefMixerGroup 
    portId = {this.props.portId}
    items={[
          {parent: 'GLOBAL', group: "ASSIGNMENT", target: "A_TRACK"},
          {parent: 'GLOBAL', group: "ASSIGNMENT", target: "A_SEND"},
           ]
          }
/>

mapStateToProps довольно прост:

const mapStateToPropy = (state, ownProps) => {
    return {
        groupItems: getItemsFromState(state.surfaces[ownProps.portId].surface, ownProps.items)
    }
}

и работа выполняется в простой функции:

const getItemsFromState = (subState, items)=>{
    let groupItems=[]
    for (let i = 0; i < items.length; i++) {
        const item = items[i];
        const base = subState[item.parent];
        
        let groupItem = base[item.group][item.target]
        groupItems.push({...groupItem, target: item.target})
    }
    return groupItems
} 

Но поскольку я создаю этот массив совпадений, я думаю, что redux думает, что я должен подписываться на каждый элемент в дереве ... когда мне нужны изменения только в найденных элементах, в этом случае:

surfaces[6].surface[GLOBAL][ASSIGNMENT][A_TRACK]
surfaces[6].surface[GLOBAL][ASSIGNMENT][A_SEND]

Я попытался использовать повторный выбор и повторный выбор вместо моей функции getItemsFromState, описанной выше, но все с тем же результатом. Любое изменение в этом дереве, начиная с поверхностей [6], запускает mapStateToProps и повторную визуализацию.

Должен быть способ обойти это, но я не могу этого понять. Я пробовал использовать areStatesEqual, но он предоставляет только nextState и prevState, а мне нужно ownProps для вычисления равенства. Возможно, я мог бы использовать areStatePropsEqual, но это работает только ПОСЛЕ перерасчета mapStateToProps без необходимости.

Должен быть способ!


person skavan    schedule 04.10.2020    source источник


Ответы (1)


getItemsFromState создает новую groupItems ссылку на массив при каждом запуске. Он будет вызываться после каждого отправленного действия. Поскольку connect выполняет повторную визуализацию каждый раз, когда любое из полей, возвращаемых mapState, изменяется на новую ссылку, ваш код заставляет React-Redux выполнять повторную визуализацию каждый раз.

Именно поэтому следует использовать мемоизированные селекторы, чтобы возвращать только новые ссылки на производные данные, если входные ссылки изменились, обычно с помощью Reselect createSelector. Если ваше использование Reselect здесь не помогает, вероятно, ваши селекторы настроены неправильно, но мне нужно увидеть конкретные примеры, чтобы дать там совет.

Именно поэтому компоненты должны подписываться. к наименьшему количеству данных, которые им действительно нужны.

Если вы используете функциональный компонент, я бы также предложил использовать useSelector вместо connect.

person markerikson    schedule 05.10.2020