V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaoming1992
V2EX  ›  TypeScript

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

  •  
  •   xiaoming1992 · 2020-05-11 23:02:13 +08:00 · 2305 次点击
    这是一个创建于 1642 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    (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)))
    }
    
    11 条回复    2020-05-14 19:09:53 +08:00
    iamppz
        1
    iamppz  
       2020-05-11 23:11:08 +08:00 via iPhone
    用 state hook,useState(new Village())
    xiaoming1992
        2
    xiaoming1992  
    OP
       2020-05-11 23:16:16 +08:00
    @iamppz useState 也没用,当我用 village 自身的方法修改了自身的数据的时候,仍然不会触发组件更新
    WittBulter
        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
    xiaoming1992
        4
    xiaoming1992  
    OP
       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
        5
    xiaoming1992  
    OP
       2020-05-11 23:25:53 +08:00
    @WittBulter 谢谢,我现在就是这样处理的,可是一方面到处都是 forceUpdate,有点丑,另一方面全程需要手动管理,感觉回到了 jQ,很难受...
    xiaoming1992
        6
    xiaoming1992  
    OP
       2020-05-11 23:28:18 +08:00
    @WittBulter 你的封装比我的帅,你看我的:

    const [uselessFlag, setUselessFlag] = useState(false)
    const updataComponent = useCallback(() => {
    setUselessFlag(!uselessFlag)
    }, [uselessFlag])
    iamppz
        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>;
    }
    ```
    theprimone
        8
    theprimone  
       2020-05-13 09:41:28 +08:00
    forceUpdate 可以再封装一个像 setState 的功能嘛,触发了自动调用 forceUpdate 。
    xiaoming1992
        9
    xiaoming1992  
    OP
       2020-05-14 00:19:55 +08:00 via Android
    @iamppz reducer 确实挺好,只是不太喜欢这种风格,貌似是目前的最优方案了


    @theprimone 对象层级比较深,对象中有数组,数组里面是对象,对象下边还有数组,深层对象的属性变动不好监听,可能又会回到下面这种样子:
    setData({
    ...data,
    key: [
    ...data[key],
    val,
    ],
    })
    这仅仅一层就已经这么丑了,要是三四层,就丑的没边了,而自动调用,前提还是得监听数据的变化,可能得试试 proxy
    theprimone
        10
    theprimone  
       2020-05-14 09:06:12 +08:00
    proxy 还没玩过,直接点比较差异的话,fast-deep-equal 应该可以。
    xiaoming1992
        11
    xiaoming1992  
    OP
       2020-05-14 19:09:53 +08:00 via Android
    @theprimone 用 deep-equal 也不可能在组件每次渲染的时候比较啊,那样性能肯定炸了,还是要在每次发生对象操作的时候比较,跟 3l 的 forceUpdate 差不多吧应该
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   934 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 21:50 · PVG 05:50 · LAX 13:50 · JFK 16:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.