iostore icon indicating copy to clipboard operation
iostore copied to clipboard

不必要渲染的如何解决?

Open xiaolshi opened this issue 6 years ago • 3 comments

因为根据store名称来收集setState,从而触发渲染的 store任何一个属性变化都将触发所有依赖组件渲染,产生不必要的渲染

xiaolshi avatar Aug 18 '19 09:08 xiaolshi

目前的解决办法只能是拆分更细粒度的 store。 另外一个办法是,添加一个 get 方法,只有使用到的数据才渲染。 此外,还可以使用 hooks 来优化一下更新条件,略复杂。

yisbug avatar Oct 12 '19 23:10 yisbug

目前的解决办法只能是拆分更细粒度的 store。 另外一个办法是,添加一个 get 方法,只有使用到的数据才渲染。 此外,还可以使用 hooks 来优化一下更新条件,略复杂。

可否通过像redux-connect那样的高阶组件来解决?

  1. 将这个部分逻辑统一交给高阶组件和useMemo(新暴露一个API)去处理
  2. 写法上可能需要转变, 不能直接在组件内useStore(这个API替换成connect)
function connect(Target, mapStateToProps) {
  return () => {
    const portionStore = mapStateToProps(useStore())
    const dependency = Object.keys(portionStore).reduce((acc, key) => {
      (acc as any).push(portionStore[key])
      return acc;
    }, [])
    return useMemo(() => <Target {...portionStore}/>, dependency)
  }
}

createStore({
  namespace: 'moduleA',
  state: {
    count: 0,
    asyncData: '',
    test: 'moduleA其他的state'
  },
  addCount() {
    this.state.count++
  },
  minusCount() {
    this.state.count--
  },
  async fetch () {
    const res = await Axios.get(TEST_API_PATH);
    this.state.asyncData = res.data.test
  }
})

function child(props) {
  console.log('子组件render')

  return (
    <div>
      <span>{props.count}</span>
      <span>{props.asyncData || '暂无'}</span>
      <br/>
      <Button onClick={() => {props.addCount()}}>+</Button>
      <Button onClick={() => {props.minusCount()}}>-</Button>
      <Button 
        onClick={() => props.fetch()} 
        loading={props.loading}
        type="primary"
      >
        async
      </Button>
    </div>
  )
}

const Child = connect(child, ({moduleA}) => ({
  count: moduleA.state.count,
  addCount: moduleA.addCount,
  minusCount: moduleA.minusCount,
  fetch: moduleA.fetch,
  loading: moduleA.fetch.loading,
  asyncData: moduleA.state.asyncData,
}));

function Parent(props) {
  console.log('父组件render')

  return (
    <div>
      <h2>父组件</h2>
      <div>{props.test}</div>
      <hr/>

      <h2>子组件</h2>
      <Child />
    </div>
  )
}

export default connect(Parent, ({moduleA}) => ({
  test: moduleA.state.test
}));

cf-19 avatar Oct 23 '19 00:10 cf-19

谢谢你的建议。用高阶组件的话,和拆分store 感觉没什么太大区别。 当然在优化思路上都是只订阅必要的数据,必要时再更新。 目前可以参考的vue,还有另外一个响应式的蛮有名的库忘记叫什么了。不好意思想不起来,有段时间没接触这块。 这个库会更新,只是可能会比较慢。我自己在实际项目中一直在使用,只是没有通过npm而是直接拷贝的源码,有需要时就直接修改了

yisbug avatar Oct 23 '19 14:10 yisbug