萌新,最近看了一下react
,感觉它很依赖于状态管理?不知道是不是因为JSX
语法的原因,函数 /类组件只能在<ComponentName />
中传入相应props
,如果父组件要访问子组件的方法就必须传入一个类似SetHandler()
的方法把子组件的方法绑定到父组件的state
上,而这样不就相当于把子组件的state
和function
移到父组件上吗?似乎没有类似SubComponentName.funName()
的访问方式...有点迷茫,可能说的不是很清除,不知道大家是怎么解决这个问题的?
第一次提问竟然有这么多回复,总之感谢大家,可能是我有点思维定势了吧,过去编程一般我会在Controller
读取设置并将它或它的一部分传递给相应Component
,然后调对应Component
的function
来解决问题,所以我觉得单向的数据流没有什么问题,问题是react
中的数据流是大概率会被改动的,这就需要一些状态逻辑,而SubComponent
中状态逻辑的方法基本只能通过它自己JSX
中事件的回调,换言之这些方法调用的位置被限定死了,所以我有点懵,在想这是不是故意设计来规避某些写法来的。
具体问题是我在用@material-ui
的Snackbar
,因为它的设置比较多,所以我稍稍封装了一下,这样就相当于有一部分状态在SubComponent
中,而SubComponent
本身又要暴露一部分状态在FatherComponent
中,比如使用open
来进行开关,然后我写了下面错误代码:
//SubComponent
function SnackBar(props) {
const [state, setState] = React.useState({
anchorOrigin: props.state.anchorOrigin || { horizontal: 'right', vertical: 'top' },
open: props.state.open || false,
Transition: props.state.Transition || Slide,
});
const handleClose = () => {
setState({
...state,
open: false,
});
};
return (
<Snackbar
open={state.open}
onClose={handleClose}
autoHideDuration={3000}
key={state.Transition.name}
>
<Alert onClose={handleClose} severity="success">
This is a success message!
</Alert>
</Snackbar>
);
}
//FatherComponent
function FatherComponent(props) {
// ...other code
return (
// ...other code
<SnackBar state={state}></SnackBar>
);
}
毫无疑问没有反应,因为这样SubComponent
的state
不会被更新,当时就想要是可以直接外部调用SubComponent
的方法设置open
就好...后来发现更新props
也算副作用来的,需要用useEffect
来更新,如果是class component
应该是在componentWillUpdate
加入setState
的逻辑。
如果一定要在父组件调用子组件的方法,可能就是ref
了,感谢大佬们~
1
Jirajine 2021-04-26 21:29:00 +08:00
React 的核心思想就在于 f(state) = ui,数据流是单向的,你得先把思维方式转变过来。
|
2
momocraft 2021-04-26 21:35:59 +08:00
像这样手工用 props 连接也是可以
react 自带的以前有 ref + class component 可能最接近你找的那种 现在有 useImperativeHandle |
3
hello2060 2021-04-26 21:40:17 +08:00 via iPhone
后端,只学过 react,没正儿八经用过。
你是不是把概念理解错了,这里的父组件是更高一级的组件,拥有子组件。相当于一个页面组件拥有 header 组件,拥有 footer 组件 而不是面向对象里的父子组件是继承关系,这里没有继承关系啊。那父组件为啥要调用子组件的功能?父组件 render 的时候子组件也自动会 render, 这不就够了吗? 数据是夫传给子的,既然数据在父那,对数据的操作自然也是父负责,所以子只要回调就行。 |
4
love 2021-04-26 22:04:01 +08:00
一定需要调用子组件的内部方法的情况非常少,绝大部分情况还是简单地维护各层 state 就行了,比传统方式简单可靠多了
|
5
JerryCha 2021-04-26 22:19:38 +08:00
恭喜你,依靠自己悟出了 React 编程的一大重点:状态提升
|
6
beizhedenglong 2021-04-26 22:20:39 +08:00
@Jirajine 你没说到点子上,vue 也是单向的
|
7
across 2021-04-26 22:38:33 +08:00
因为 React 只是个 View 层数据流,在 MVC 视角下,你是想把 VC 都放在 ReactComponent 下···· 这事儿就不应该放 View 层做。
|
8
qiuxuqin 2021-04-26 22:40:01 +08:00
@beizhedenglong vue 的子组件通过 emit 事件,然后父组件接收事件进行处理,这里哪里是单向的了?而且父组件可以直接通过 this.$refs.childComponent 获取到子组件内部的各种变量和方法,这跟单向流动差远了。
|
9
beizhedenglong 2021-04-26 22:45:23 +08:00
@qiuxuqin 按照你这这个逻辑,react 通过 callback 回调传数据和 emit 事件传数据又有啥本质区别?
|
10
agdhole 2021-04-27 00:07:40 +08:00
搜一下 immutable
|
11
Rocketer 2021-04-27 01:33:39 +08:00 via iPhone
状态管理算是个 hack,初学先别考虑那个。
React 最重要的基本概念之一就是数据单向流动,只能从父流向子。所以几个组件要想共享数据,就得找个共同的祖先来持有那个数据。 至于更新状态的函数,你也当数据来用就行了,毕竟 JavaScript 里一切皆对象,字符串和函数一视同仁。 |
13
Rocketer 2021-04-27 02:29:11 +08:00 via iPhone
@mongodb 状态管理本质上就是这么个东西啊。所以说初学先别考虑状态管理,等彻底理解了 React 再看。
|
14
seki 2021-04-27 03:28:35 +08:00
如果一个组件 props 在更新,自己的 state 也在更新,两部分想归到同一个流里面需要思考很多情况,强行写出来性能可能也会比较差
把组件分离成专门用于显示的内容的,以及专门用来处理数据的会让逻辑更简单,实现单向数据流也会更少心智负担 再复杂到一定阶段就会用 context 和 redux / recoil / mobx 之类来把数据处理单独抽出来了 |
15
walpurgis 2021-04-27 08:48:02 +08:00 via Android
https://zh-hans.reactjs.org/docs/lifting-state-up.html
数据和逻辑提到父组件是官方推荐做法 |
16
gouflv 2021-04-27 09:06:20 +08:00 via iPhone
1. 如果子组件本身足够复杂,外部可以通过注入类似服务的对象,由服务来间接控制子组件,参考 antd 的 useForm
2. 简单的子组件,用 useImperativeHandle 暴露 api |
17
jguo 2021-04-27 09:08:05 +08:00
父组件调用子组件的方法意味着强耦合,不符合 react 的思路
|
18
forsigner 2021-04-27 10:05:38 +08:00
可以开始了解 React 之状态管理大乱斗了
|
19
shunia 2021-04-27 10:20:40 +08:00 1
这和状态管理有啥关系。。。你只要把 <Component /> 理解成 function Component() {} 就好了。
|
20
DOLLOR 2021-04-27 10:39:58 +08:00
父组件没必要去访问子组件内部的状态。
就像你调用函数的时候,函数外部没必要去获取函数内部的局部变量。 |
21
ccraohng 2021-04-27 10:45:15 +08:00 1
只关心组件自己的状态维护,如果你只是改变子组件的数据,可以改成受控组件。比较复杂的函数操作,比如 `slider.next()`,通过 ref 暴露就行。
|
22
zhaol 2021-04-27 11:39:14 +08:00
// 子组件
class ComponentName { componentDidMount() { this.props.onReady && this.props.onReady(this) } aaa = () => {} } //父组件 class Father { subComponentName = null componentDidMount() { this.subComponentName.aaa() } render() { return <ComponentName onReady={(instance)=>{this.subComponentName = instance}}/> } } |
24
towry 2021-04-27 14:52:02 +08:00
看看 react 的 ref 是怎么实现的。
|
25
DreamTrace OP @zhaol 没有看懂......`onReady`似乎是`props`上的一个变量,我应该看哪里?
|
26
zhaol 2021-04-28 10:13:32 +08:00
@DreamTrace 就是 ref 的手动实现,子组件通过 onready 把组件实例抛到父组件用一个变量(subComponentName)保存,这样你就可以通过这个变量调用子组件里面的属性和方法了
|
27
qiuxuqin 2021-04-28 11:24:48 +08:00
@beizhedenglong react 的数据变更函数是通过父组件传到子组件的,在子组件调用 setState 函数实际上是调用父组件的方法,更改父组件的数据,然后传给子组件的那个数据也跟着变了,所以 React 实际上还是数据单向传送的。vue 的 emit 是在子组件通过事件通知父组件,它这个数据变更是通过子组件往上传的,所以是双向流动。
|
28
qiuxuqin 2021-04-28 12:04:11 +08:00
@beizhedenglong 可能我这个解释也不一定准确。我再解释一下:vue 的 v-model 语法其实就算是双向绑定,虽然说它是$props.value 和 emit('change', value )的语法糖。另外如果某个 prop 是个对象,是可以直接修改这个对象的 key 的,这样父组件和子组件也可以的这个对象值也会改变,视图也会更新(当然如果是原始值会报错)。
|
29
beizhedenglong 2021-04-28 12:24:23 +08:00 via iPhone
@qiuxuqin VUE 实现导致的,它不建议你直接改,react 你也可以直接改
|
30
qiuxuqin 2021-04-28 19:10:33 +08:00
@beizhedenglong vue 修改 prop 对象内部的值,它还是响应式的,视图会更新。react 直接修改 prop,视图并不会更新,要用 setState 才会触发视图更新。
|