由于一些原因,我希望把逻辑和视图分开,如下列伪代码所示,大佬能说说有什么好的解决方案吗?关键字就行,谢谢了!
(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)))
}
1
iamppz 2020-05-11 23:11:08 +08:00 via iPhone
用 state hook,useState(new Village())
|
2
xiaoming1992 OP @iamppz useState 也没用,当我用 village 自身的方法修改了自身的数据的时候,仍然不会触发组件更新
|
3
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 |
4
xiaoming1992 OP @iamppz 比方说
const village = new Village() const familyA = village.datas[3] const personS = familyA.datas[4] personS.age += 1 这样的情况,我目前的处理是,在每个操作时,手动通知组件更新,可是这也太傻了... (ps: 可以全量更新 village, 如 setVillage(newVillage), 但是对于复杂的, 数组和对象混杂的对象, 这样做很累, 还丑) |
5
xiaoming1992 OP @WittBulter 谢谢,我现在就是这样处理的,可是一方面到处都是 forceUpdate,有点丑,另一方面全程需要手动管理,感觉回到了 jQ,很难受...
|
6
xiaoming1992 OP @WittBulter 你的封装比我的帅,你看我的:
const [uselessFlag, setUselessFlag] = useState(false) const updataComponent = useCallback(() => { setUselessFlag(!uselessFlag) }, [uselessFlag]) |
7
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>; } ``` |
8
theprimone 2020-05-13 09:41:28 +08:00
forceUpdate 可以再封装一个像 setState 的功能嘛,触发了自动调用 forceUpdate 。
|
9
xiaoming1992 OP @iamppz reducer 确实挺好,只是不太喜欢这种风格,貌似是目前的最优方案了
@theprimone 对象层级比较深,对象中有数组,数组里面是对象,对象下边还有数组,深层对象的属性变动不好监听,可能又会回到下面这种样子: setData({ ...data, key: [ ...data[key], val, ], }) 这仅仅一层就已经这么丑了,要是三四层,就丑的没边了,而自动调用,前提还是得监听数据的变化,可能得试试 proxy |
10
theprimone 2020-05-14 09:06:12 +08:00
proxy 还没玩过,直接点比较差异的话,fast-deep-equal 应该可以。
|
11
xiaoming1992 OP @theprimone 用 deep-equal 也不可能在组件每次渲染的时候比较啊,那样性能肯定炸了,还是要在每次发生对象操作的时候比较,跟 3l 的 forceUpdate 差不多吧应该
|