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

2024 年 4 月 7 日
 mouyase

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

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

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

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

1906 次点击
所在节点    问与答
23 条回复
rabbbit
2024 年 4 月 7 日
没太看懂,要我写就是
1 独立的表单组件,可以传值进来,用户编辑后抛出事件,别的值从哪来、存到哪一律不管。
2 取值、缓存值这些操作独立出来放外面,根据情况看封装个 hook 还是塞到状态管理里啥的。
mouyase
2024 年 4 月 7 日
@rabbbit

如果传值进来的话,是通过 props 传进来吗?那这个值是不是需要是一个 state ?那我修改 state 的时候,会导致这个组件和父级组件都重新渲染,会导致重复的生命周期。
事件传出去的值是不是也要用 state 保存呢?不然应该如何根据一个组件的值修改另一个组件呢。
huijiewei
2024 年 4 月 7 日
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
2024 年 4 月 7 日
大概这个意思,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
2024 年 4 月 7 日
@huijiewei 感谢解惑。

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

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
2024 年 4 月 8 日
@rabbbit #16 那就组件内用 useMemo 咯,只引用各自用到的 a 和 b ,另外 setInputValue 直接用 prevState

https://gist.github.com/wkmike/e3787e498dd7288e990a96d2cfe5ec3b
Mandmg
2024 年 4 月 8 日
用 redux 啊..
直接解决了
huijiewei
2024 年 4 月 8 日
@mouyase C D E F 并不会重新渲染,根组件的渲染不会影响子组件,除非你也改变了子组件的 props ,这就是 React 为啥要细粒度组件的原因
br_wang
2024 年 4 月 8 日
参考下 ElementUI 或 AntDesign 里 Form 组件的设计,一般内部还是会维护一个 state ,处理编辑的状态记录,还会有类似 isDirty 的 flag 用于标注该项是否由用户编辑过,编辑过就要处理校验等等。

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

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

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

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

© 2021 V2EX