Подтвердить что ты не робот

Проблемы производительности с древовидной структурой и shouldComponentUpdate в React/Redux

Я новичок в React, Redux и ImmutableJS и столкнулся с некоторыми проблемами производительности.

У меня есть большая древовидная структура данных, которую я сейчас храню в виде плоского списка в этой структуре:

new Map({
  1: new Node({
    id: 1,
    text: 'Root',
    children: [2,3]
  }),
  2: new Node({
    id: 2,
    text: 'Child 1',
    children: [4]
  }),
  3: new Node({
    id: 3,
    text: 'Child 2',
    children: []
  }),
  4: new Node({
    id: 4,
    text: 'Child of child 1',
    children: []
  })
});

При структурировании его как плоского списка упрощается обновление узлов, я обнаруживаю, что взаимодействие становится вялым по мере роста дерева. Взаимодействие включает в себя возможность выбора одного или нескольких узлов, переключения видимости их дочерних узлов, обновления текста и т.д. Похоже, что ключевой причиной вялого интерфейса является то, что все дерево перерисовывается для каждого взаимодействия.

Я хочу использовать shouldComponentUpdate, чтобы при обновлении node 3 узлы 2 и 4 не обновлялись. Это было бы легко, если бы данные были сохранены как дерево (я мог бы просто проверить, есть ли this.props !== nextProps), но поскольку данные хранятся в плоском списке, проверка будет значительно сложнее.

Как мне хранить данные и использовать shouldComponentUpdate (или другие методы) для поддержки гладкого интерфейса с сотнями или тысячами узлов дерева?

Edit

Я подключил хранилище на верхнем уровне, а затем должен передать весь хранилище до подкомпонентов.

Моя структура:

<NodeTree> 
  <NodeBranch>
    <Node>{{text}}</Node>
    <NodeTree>...</NodeTree>
  </NodeBranch>
  <NodeBranch>
    <Node>{{text}}</Node>
    <NodeTree>...</NodeTree>
  </NodeBranch>
  ...
</NodeTree>

<Node> может выполнить простую проверку с помощью shouldComponentUpdate, чтобы узнать, изменился ли заголовок, но у меня нет аналогичного решения для использования в <NodeTree> или <NodeBranch>, учитывая рекурсивный характер дерева.

Похоже, что лучшим решением (спасибо @Dan Abramov) было бы подключение каждого <NodeBranch>, а просто подключение на верхнем уровне. Я проведу это сегодня вечером.

4b9b3361

Ответ 1

I просто добавил новый пример, показывающий именно это.
Вы можете запустить его следующим образом:

git clone https://github.com/rackt/redux.git

cd redux/examples/tree-view
npm install
npm start

open http://localhost:3000/

Ответ 2

Решение Дэн Абрамова в 99% случаев обычно прекращается.

Но время вычисления, требуемое для каждого приращения, пропорционально O (n) количеству узлов в дереве, поскольку каждый подключенный node HOC должен выполнить немного кода (например, сопоставление идентичности...). Однако этот код довольно быстро выполняется.

Если вы протестируете решение Дэн Абрамов, с 10k узлами на моем ноутбуке, я начинаю видеть некоторую задержку при попытке увеличить счетчик (например, задержка в 100 мс). Вероятно, это намного хуже на дешевых мобильных устройствах.

Можно утверждать, что вы не должны пытаться отображать 10 000 элементов в DOM сразу, и я полностью согласен с этим, но если вы действительно хотите отображать множество элементов и подключать их, это не так просто.

Если вы хотите повернуть этот O (n) в O (1), вы можете реализовать свою собственную систему connect, где HOC (компонент более высокого порядка ) подписка активируется только при обновлении базового счетчика вместо всех подписчиков HOC.

Я рассказал, как это сделать в этом .

Я создал issue для реакции-сокращения, чтобы connect стал более гибким и позволял легко настраивать подписку на магазин через опции. Идея состоит в том, что вы должны иметь возможность повторно использовать код connect и эффективно подписываться на изменения срезов штата вместо глобальных изменений состояния (т.е. store.subscribe()).

const mapStateToProps = (state,props) => {node: selectNodeById(state,props.nodeId)}

const connectOptions = {
   doSubscribe: (store,props) => store.subscribeNode(props.nodeId)
}

connect(mapStateToProps, undefined,connectOptions)(ComponentToConnect)

По-прежнему ваша собственная ответственность создать энхансер магазина с помощью метода subscribeNode.