在项目中遇到了类似这样的需求。
有一个类似表单的页面,这个页面里的每一个组件的数据源都可能会有以下几种情况。
基本的逻辑就是页面里总是显示最后一个激活的值,但是这种情况下不知道如何设计组件才能达到一个比较好的效果了。
之前试着把数据放进了 ref ,但是这样数据改变又很难刷新,不知道有没有什么好办法来处理呢。
1
rabbbit 258 天前
没太看懂,要我写就是
1 独立的表单组件,可以传值进来,用户编辑后抛出事件,别的值从哪来、存到哪一律不管。 2 取值、缓存值这些操作独立出来放外面,根据情况看封装个 hook 还是塞到状态管理里啥的。 |
2
mouyase OP @rabbbit
如果传值进来的话,是通过 props 传进来吗?那这个值是不是需要是一个 state ?那我修改 state 的时候,会导致这个组件和父级组件都重新渲染,会导致重复的生命周期。 事件传出去的值是不是也要用 state 保存呢?不然应该如何根据一个组件的值修改另一个组件呢。 |
3
huijiewei 258 天前
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 根据数据渲染即可 |
4
rabbbit 258 天前
大概这个意思,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> ); } |
5
mouyase OP @huijiewei 感谢解惑。
如果是操作 A 组件导致 B 组件变换。 <Comp> <A /> <B /> <C /> <D /> <E /> <F /> </Comp> 如果是这种组件结构的话,A 去修改 B 的 props 值,则需要把 state 放到 Comp 里吧。但是如果是这样,state 就会导致整个 Comp 都会重新渲染,最后导致 CDEF 组件全都重渲染,这种情况有什么好的办法吗。 |
7
rabbbit 258 天前
其实没有性能压力表单啥的重新渲染也就渲染了
|
8
rabbbit 258 天前
有性能压力的话我就不太清楚了,我这用 vue ,react 写的少。
有性能要求的时候我一般都是直接在 vue 里写原生 js 操作 DOM 或者用 lit 这种 web component 这种。 |
9
rabbbit 258 天前
|
10
wuyiccc 258 天前
用 useMemo 控制一下防止组件重复渲染?
|
13
wkmike 258 天前
@mouyase #12 如果都是外部组件并且接收同样的 Props 比如你说的 state 对象,A 组件根据 state.a 来更新,那你在 React.memo 第二个参数里面只判断 prevProps.state.a 和 nextProps.state.a 是否变化就行了
|
15
wkmike 258 天前
@mouyase #14 React.memo 就是给 Function Component 实现了类似 Class Component 生命周期 shouldComponentUpdate 方法用来优化性能的高阶组件
|
16
rabbbit 258 天前
试着研究了一下,这样倒是不会更新,不过感觉好麻烦
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> ); } |
17
wkmike 258 天前 1
@rabbbit #16 那就组件内用 useMemo 咯,只引用各自用到的 a 和 b ,另外 setInputValue 直接用 prevState
https://gist.github.com/wkmike/e3787e498dd7288e990a96d2cfe5ec3b |
18
Mandmg 258 天前
用 redux 啊..
直接解决了 |
20
br_wang 258 天前
参考下 ElementUI 或 AntDesign 里 Form 组件的设计,一般内部还是会维护一个 state ,处理编辑的状态记录,还会有类似 isDirty 的 flag 用于标注该项是否由用户编辑过,编辑过就要处理校验等等。
|
21
mouyase OP @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 组件内部按钮按下时,组件渲染两次。 |