ReactNative 开发时,如何设计一个数据“灵活”的组件呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mouyase
V2EX    问与答

ReactNative 开发时,如何设计一个数据“灵活”的组件呢?

  •  
  •   mouyase 2024-04-07 22:24:13 +08:00 1498 次点击
    这是一个创建于 556 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在项目中遇到了类似这样的需求。

    有一个类似表单的页面,这个页面里的每一个组件的数据源都可能会有以下几种情况。

    • 当前页面没有任何操作时的初始值
    • 当前页面从外部跳转过来时,使用外部跳转过来的值
    • 当前页面编辑后,缓存到本地的值
    • 页面里的交互逻辑,比如操作了 A 组件导致 B 组件变化了的值

    基本的逻辑就是页面里总是显示最后一个激活的值,但是这种情况下不知道如何设计组件才能达到一个比较好的效果了。

    之前试着把数据放进了 ref ,但是这样数据改变又很难刷新,不知道有没有什么好办法来处理呢。

    23 条回复    2024-04-08 20:44:28 +08:00
    rabbbit
        1
    rabbbit  
       2024-04-07 22:41:28 +08:00
    没太看懂,要我写就是
    1 独立的表单组件,可以传值进来,用户编辑后抛出事件,别的值从哪来、存到哪一律不管。
    2 取值、缓存值这些操作独立出来放外面,根据情况看封装个 hook 还是塞到状态管理里啥的。
    mouyase
        2
    mouyase  
    OP
       2024-04-07 22:47:30 +08:00
    @rabbbit

    如果传值进来的话,是通过 props 传进来吗?那这个值是不是需要是一个 state ?那我修改 state 的时候,会导致这个组件和父级组件都重新渲染,会导致重复的生命周期。
    事件传出去的值是不是也要用 state 保存呢?不然应该如何根据一个组件的值修改另一个组件呢。
    huijiewei
        3
    huijiewei  
       2024-04-07 23:02:45 +08:00
    React 的组件只有 2 种值

    1. 外部传入 props 或者 useContext 的跨组件值,这些都是外部值
    2. 内部变化 state

    你所说的所有情况都可以抽象为这种

    当前页面没有任何操作时的初始值
    当前页面从外部跳转过来时,使用外部跳转过来的值
    -----
    上面两种情况是一样的,都是初始值,放在 props 传入


    当前页面编辑后,缓存到本地的值
    -----
    其实就是内部 state 的缓存,这里的缓存看你控制的细粒度了,可以整个表单的 state 缓存,也可以单独组件的 state 缓存,这里抽象会复杂一些,性能要求不高,表单控件不多的话,就用表单缓存


    页面里的交互逻辑,比如操作了 A 组件导致 B 组件变化了的值
    -----
    A 引起 B 变化就是 B 的 props 引入了根据 A 变化的值,这种情况下 B 肯定要重新渲染的


    React 的特性需要抽象成最小细粒的组件,以组件为单位渲染即可。

    建议使用现成的 Form 组件或者 react-hook-form ,UI 根据数据渲染即可
    rabbbit
        4
    rabbbit  
       2024-04-07 23:05:14 +08:00
    大概这个意思,input 组件啥都不管

    import React,{ useState } from 'react';

    function MyInput(props) {
    return (
    <input value={props.value} OnInput={(e) => {
    props.onInput(e.target.value)
    }} />
    )
    }
    export function App(props) {
    const [inputValue, setInputValue] = useState('1')
    return (
    <div className='App'>
    <MyInput value={inputValue} OnInput={(value) => {
    setInputValue(value)
    }}/>
    </div>
    );
    }
    mouyase
        5
    mouyase  
    OP
       2024-04-07 23:07:34 +08:00
    @huijiewei 感谢解惑。

    如果是操作 A 组件导致 B 组件变换。
    <Comp>
    <A />
    <B />
    <C />
    <D />
    <E />
    <F />
    </Comp>
    如果是这种组件结构的话,A 去修改 B 的 props 值,则需要把 state 放到 Comp 里吧。但是如果是这样,state 就会导致整个 Comp 都会重新渲染,最后导致 CDEF 组件全都重渲染,这种情况有什么好的办法吗。
    mouyase
        6
    mouyase  
    OP
       2024-04-07 23:08:24 +08:00
    @rabbbit 这种情况下,MyInput 每次输入都会导致 App 组件整体重渲染吧。
    rabbbit
        7
    rabbbit  
       2024-04-07 23:08:43 +08:00
    其实没有性能压力表单啥的重新渲染也就渲染了
    rabbbit
        8
    rabbbit  
       2024-04-07 23:12:09 +08:00
    有性能压力的话我就不太清楚了,我这用 vue ,react 写的少。
    有性能要求的时候我一般都是直接在 vue 里写原生 js 操作 DOM 或者用 lit 这种 web component 这种。
    rabbbit
        9
    rabbbit  
       2024-04-07 23:18:07 +08:00
    也许 useMemo 能解决你说的子组件被更新的问题
    参考一下这个看看?
    t/854260
    wuyiccc
        10
    wuyiccc  
       2024-04-07 23:20:33 +08:00
    用 useMemo 控制一下防止组件重复渲染?
    wkmike
        11
    wkmike  
       2024-04-07 23:20:39 +08:00
    @mouyase #5 ABCDEF 都用 React.memo 包一下,各自定制 Props 变更判断
    mouyase
        12
    mouyase  
    OP
       2024-04-07 23:33:09 +08:00
    @wkmike 那是不是所有的组件的 props 都要独立成不同的 state 呢,如果所有的 state 都在同一个 Object 里,是不是就无效了?
    wkmike
        13
    wkmike  
       2024-04-07 23:45:35 +08:00
    @mouyase #12 如果都是外部组件并且接收同样的 Props 比如你说的 state 对象,A 组件根据 state.a 来更新,那你在 React.memo 第二个参数里面只判断 prevProps.state.a 和 nextProps.state.a 是否变化就行了
    mouyase
        14
    mouyase  
    OP
       2024-04-07 23:55:53 +08:00
    @wkmike 是不是只有类组件才能这样处理,如果是函数组件呢
    wkmike
        15
    wkmike  
       2024-04-08 00:11:43 +08:00
    @mouyase #14 React.memo 就是给 Function Component 实现了类似 Class Component 生命周期 shouldComponentUpdate 方法用来优化性能的高阶组件
    rabbbit
        16
    rabbbit  
       2024-04-08 00:35:27 +08:00
    试着研究了一下,这样倒是不会更新,不过感觉好麻烦

    import React,{ useState, memo } from 'react';

    function MyInput(props) {
    return (
    <input value={props.value} OnInput={(e) => {
    props.onInput(e.target.value)
    }} />
    )
    }

    function MyInputB(props) {
    return (
    <input value={props.value} OnInput={(e) => {
    props.onInput(e.target.value)
    }} />
    )
    }

    const InputBMemo = memo(({value, onInput}) => {
    return <MyInputB value={value} OnInput={onInput}/>
    },(oldProps, newProps) => oldProps.value === newProps.value)

    export function App(props) {
    const [inputValue, setInputValue] = useState({
    a: 1,
    b: 2
    })

    return (
    <div className='App'>
    <MyInput value={inputValue.a} OnInput={(value) => {
    setInputValue({
    ...inputValue,
    a: value
    })
    }}/>

    <InputBMemo value={inputValue.b} OnInput={(value) => {
    setInputValue({
    ...inputValue,
    b: value
    })
    }}/>
    </div>
    );
    }
    wkmike
        17
    wkmike  
       2024-04-08 01:10:44 +08:00   1
    @rabbbit #16 那就组件内用 useMemo 咯,只引用各自用到的 a 和 b ,另外 setInputValue 直接用 prevState

    https://gist.github.com/wkmike/e3787e498dd7288e990a96d2cfe5ec3b
    Mandmg
        18
    Mandmg  
       2024-04-08 09:13:29 +08:00
    用 redux 啊..
    直接解决了
    huijiewei
        19
    huijiewei  
       2024-04-08 09:25:11 +08:00
    @mouyase C D E F 并不会重新渲染,根组件的渲染不会影响子组件,除非你也改变了子组件的 props ,这就是 React 为啥要细粒度组件的原因
    br_wang
        20
    br_wang  
       2024-04-08 10:51:23 +08:00
    参考下 ElementUI 或 AntDesign 里 Form 组件的设计,一般内部还是会维护一个 state ,处理编辑的状态记录,还会有类似 isDirty 的 flag 用于标注该项是否由用户编辑过,编辑过就要处理校验等等。
    mouyase
        21
    mouyase  
    OP
       2024-04-08 19:15:05 +08:00 via Android
    @huijiewei 刚刚这边做着做着就有了一个疑问。

    假设还是上面的结构。

    A 组件内部有一个 state ,一个按钮和一个文本,state 用来判断一个文本的展示与否。同时我需要外面(比如 B 组件)接收到这个 state 用于逻辑处理。所以我应该在按钮点击事件的时候,修改 state ,同时调用父组件传入的 onChange 函数,传出 state 。

    但是如果这种情况下,我还需要 C 按钮也可以切换这个 A 组件的 state ,我应该怎么处理比较好?

    我现在的做法是,在父组件放一个 state ,然后 A 组件 onChange 的时候,将传出的数值赋值到这个 state 上,同时,又把这个 state 传入 A 组件作为 prop ,然后在 a 组件用 useEffect ,当这个值变化的时候再给 A 组件内的 state 赋值。然后 C 操作的时候修改父级的 state 。

    这样似乎会导致 A 组件内部按钮按下时,组件渲染两次。
    huijiewei
        22
    huijiewei  
       2024-04-08 20:15:57 +08:00
    @mouyase 开发的时候使用了 StrictMode ,都会渲染两次。
    mouyase
        23
    mouyase  
    OP
       2024-04-08 20:44:28 +08:00
    @huijiewei 不是严格模式的问题,state 被连续赋值了两次
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5909 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 78ms UTC 02:01 PVG 10:01 LAX 19:01 JFK 22:01
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86