mobxjs / mobx-react

React bindings for MobX
https://mobx.js.org/react-integration.html
MIT License
4.85k stars 349 forks source link

A better way to trigger an autorun? #785

Closed noah79 closed 5 years ago

noah79 commented 5 years ago

We are using react-window to dynamically group and query data from an Apollo backend. The catch is that the height of a given 'virtual row' is dependent oftentimes dependent upon the render height of expanded children.

You can see some of our implemented behavior here:

virtualizedListDemo

I have a dictionary of node ids mapped to render dimensions: nodes = observable.map<string, { definition: ProductLikeDefinition, renderDimensions?: { width: number, height: number } }>()

These dimensions are updated later when expanded groups are rendered:

useLayoutEffect(() => {
    if (!props.hideChildren && node && height) {
      const {renderDimensions} = node

      if (!renderDimensions || height != renderDimensions.height) {
        node.renderDimensions = {height, width}
      }
    }
  }, [props.hideChildren, node, height])

The only API I have to react-window is to tell it when to reset it's row height cache. So, for example, I can't just tell it that row 4 has changed it's height, instead I need to detect that any of my dependencies have changed, at which point I can tell the list to repaint.

So, here's how I've implemented it so far - and it does work, but...:

 useEffect(function ResetSizesOnExpansion() {
      return autorun(() => {
        const { groupedSpec, childGroupedSpec } = store
        if (groupedSpec) {
          const {expandedSize, nodes: {size: groupSize} } = groupedSpec
          Array.from(groupedSpec.nodes.values()).map(n => n.renderDimensions && n.renderDimensions.height)
          if (childGroupedSpec) {
            const {expandedSize: childExpandedSize, nodes} = childGroupedSpec
            Array.from(nodes.values()).map(n => n.renderDimensions && n.renderDimensions.height)
          }

          // Touch any dependencies that will require us to recalculate our sizes
          // noinspection JSUnusedLocalSymbols
          const {dashboard: {cardViewWithAttributes, groupBy: {hierarchy, showExpandedFirst, expandAllLevel, viewMode, cardSize: {zoom: productCardZoom}}}, specDashboard: {cardSize: {zoom: specCardZoom}}} = stores,
                {list, group, virtualRowCount, cardPanelStore: {cardsPerRow}}                                                                                                     = store

          if (list) {
            store.updateCardPanelStore()
            //store.debouncedResetHeights()
            list.resetAfterIndex(0, true)
          }
        }
      }, {name: `Reset virtual list row heights on expansion`})
    })

But...I'm walking the entire node dictionary and touching every renderDimension height so that my autorun will get retriggered again later if any of them change. Thing is, as soon as I detect a change I would love to bail out and stop walking the arrays.

Can anyone suggest some better ways to accomplish this? We pull the renderDimension out later in a computedFn callback from the virtualized list when it asks for a given row index's height.

danielkcz commented 5 years ago

Yea, let's close this one and keep discussion concentrated at https://github.com/mobxjs/mobx/issues/2134 given it's mostly MobX-only related.

lock[bot] commented 4 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs or questions.