希望把逻辑和视图分开,逻辑类的实例改变后, react 如何通知组件更新?

2020-05-11 23:02:13 +08:00
 xiaoming1992

由于一些原因,我希望把逻辑和视图分开,如下列伪代码所示,大佬能说说有什么好的解决方案吗?关键字就行,谢谢了!

(ps: react class component 也是视图和逻辑混在一个类里面, 不太希望这样, 还是我的使用姿势不对?)

import React, { useEffect } from "react"

class Person {
  name: string

  age: number

  friends: Person[]
}

class Family {
  datas: Person[]
}

class Village {
  datas: Family[]
}

// 以上的类为纯逻辑类, 仅进行数据处理, 不含视图渲染

function View() {
  const village = new Village()

  useEffect(() => {
    // Village 泛指一些层级很深的类
    // 这儿有一些交互, 然后会修改 village 的一些属性, 和属性的后代属性
    // 问题是, 修改了 village 之后, 怎么通知组件更新呢...
  }, [village])

  return  village.datas.map((family) => family.datas.map((person) => render(person)))
}
2329 次点击
所在节点    TypeScript
11 条回复
iamppz
2020-05-11 23:11:08 +08:00
用 state hook,useState(new Village())
xiaoming1992
2020-05-11 23:16:16 +08:00
@iamppz useState 也没用,当我用 village 自身的方法修改了自身的数据的时候,仍然不会触发组件更新
WittBulter
2020-05-11 23:17:29 +08:00
当你在 `React FC` 中调用 `setState` 不同的值会就触发一次更新,或者 `Props` 改变也会触发,基于这个原理你可以写一个非常简单 hooks 来解决这件事:

```
const [, setState] = useState({})
const forceUpdate = useCallback(() => setState({}), [])
```

我在 codesendbox 上给你写了一个 hooks 的例子: https://codesandbox.io/s/force-update-react-7e8zs?file=/src/app.js
xiaoming1992
2020-05-11 23:23:24 +08:00
@iamppz 比方说

const village = new Village()

const familyA = village.datas[3]

const personS = familyA.datas[4]

personS.age += 1

这样的情况,我目前的处理是,在每个操作时,手动通知组件更新,可是这也太傻了...

(ps: 可以全量更新 village, 如 setVillage(newVillage), 但是对于复杂的, 数组和对象混杂的对象, 这样做很累, 还丑)
xiaoming1992
2020-05-11 23:25:53 +08:00
@WittBulter 谢谢,我现在就是这样处理的,可是一方面到处都是 forceUpdate,有点丑,另一方面全程需要手动管理,感觉回到了 jQ,很难受...
xiaoming1992
2020-05-11 23:28:18 +08:00
@WittBulter 你的封装比我的帅,你看我的:

const [uselessFlag, setUselessFlag] = useState(false)
const updataComponent = useCallback(() => {
setUselessFlag(!uselessFlag)
}, [uselessFlag])
iamppz
2020-05-12 06:37:23 +08:00
@xiaoming1992 state 应该是不能直接修改的,你可以用 immutability-helper 修改并拿到 village 的一个新的克隆对象,然后再调用 setState 触发页面的刷新。

另外如果目的只是视图和逻辑分离的话,是否可以考虑将 reducer 函数从视图文件中分离出去:

例如 biz.js
```
function reducer(state, action) {
switch (action):
case 'xxx':
// immutability-helper
update(state, {
x: {y: {z: {$set: 7}}},
});
break;
default:
break;
return {...state};
}
```

视图中:
```
function View() {
const [data, dispatch] = useReducer(reducer, new Village());
return <span>{JSON.stringify(data)}</span>;
}
```
theprimone
2020-05-13 09:41:28 +08:00
forceUpdate 可以再封装一个像 setState 的功能嘛,触发了自动调用 forceUpdate 。
xiaoming1992
2020-05-14 00:19:55 +08:00
@iamppz reducer 确实挺好,只是不太喜欢这种风格,貌似是目前的最优方案了


@theprimone 对象层级比较深,对象中有数组,数组里面是对象,对象下边还有数组,深层对象的属性变动不好监听,可能又会回到下面这种样子:
setData({
...data,
key: [
...data[key],
val,
],
})
这仅仅一层就已经这么丑了,要是三四层,就丑的没边了,而自动调用,前提还是得监听数据的变化,可能得试试 proxy
theprimone
2020-05-14 09:06:12 +08:00
proxy 还没玩过,直接点比较差异的话,fast-deep-equal 应该可以。
xiaoming1992
2020-05-14 19:09:53 +08:00
@theprimone 用 deep-equal 也不可能在组件每次渲染的时候比较啊,那样性能肯定炸了,还是要在每次发生对象操作的时候比较,跟 3l 的 forceUpdate 差不多吧应该

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/670737

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX