关于 react 编程思路的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
DreamTrace
V2EX    React

关于 react 编程思路的问题

  •  1
     
  •   DreamTrace 2021-04-26 21:11:19 +08:00 4152 次点击
    这是一个创建于 1633 天前的主题,其中的信息可能已经有所发展或是发生改变。

    萌新,最近看了一下react,感觉它很依赖于状态管理?不知道是不是因为JSX语法的原因,函数 /类组件只能在<ComponentName />中传入相应props,如果父组件要访问子组件的方法就必须传入一个类似SetHandler()的方法把子组件的方法绑定到父组件的state上,而这样不就相当于把子组件的statefunction移到父组件上吗?似乎没有类似SubComponentName.funName()的访问方式...有点迷茫,可能说的不是很清除,不知道大家是怎么解决这个问题的?

    第 1 条附言    2021-04-27 23:59:34 +08:00

    第一次提问竟然有这么多回复,总之感谢大家,可能是我有点思维定势了吧,过去编程一般我会在Controller读取设置并将它或它的一部分传递给相应Component,然后调对应Componentfunction来解决问题,所以我觉得单向的数据流没有什么问题,问题是react中的数据流是大概率会被改动的,这就需要一些状态逻辑,而SubComponent中状态逻辑的方法基本只能通过它自己JSX中事件的回调,换言之这些方法调用的位置被限定死了,所以我有点懵,在想这是不是故意设计来规避某些写法来的。

    具体问题是我在用@material-uiSnackbar,因为它的设置比较多,所以我稍稍封装了一下,这样就相当于有一部分状态在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> ); } 

    毫无疑问没有反应,因为这样SubComponentstate不会被更新,当时就想要是可以直接外部调用SubComponent的方法设置open就好...后来发现更新props也算副作用来的,需要用useEffect来更新,如果是class component应该是在componentWillUpdate加入setState的逻辑。

    如果一定要在父组件调用子组件的方法,可能就是ref了,感谢大佬们~

    30 条回复    2021-04-28 19:10:33 +08:00
    Jirajine
        1
    Jirajine  
       2021-04-26 21:29:00 +08:00
    React 的核心思想就在于 f(state) = ui,数据流是单向的,你得先把思维方式转变过来。
    momocraft
        2
    momocraft  
       2021-04-26 21:35:59 +08:00
    像这样手工用 props 连接也是可以

    react 自带的以前有 ref + class component 可能最接近你找的那种

    现在有 useImperativeHandle
    hello2060
        3
    hello2060  
       2021-04-26 21:40:17 +08:00 via iPhone
    后端,只学过 react,没正儿八经用过。

    你是不是把概念理解错了,这里的父组件是更高一级的组件,拥有子组件。相当于一个页面组件拥有 header 组件,拥有 footer 组件

    而不是面向对象里的父子组件是继承关系,这里没有继承关系啊。那父组件为啥要调用子组件的功能?父组件 render 的时候子组件也自动会 render, 这不就够了吗?

    数据是夫传给子的,既然数据在父那,对数据的操作自然也是父负责,所以子只要回调就行。
    love
        4
    love  
       2021-04-26 22:04:01 +08:00
    一定需要调用子组件的内部方法的情况非常少,绝大部分情况还是简单地维护各层 state 就行了,比传统方式简单可靠多了
    JerryCha
        5
    JerryCha  
       2021-04-26 22:19:38 +08:00
    恭喜你,依靠自己悟出了 React 编程的一大重点:状态提升
    beizhedenglong
        6
    beizhedenglong  
       2021-04-26 22:20:39 +08:00
    @Jirajine 你没说到点子上,vue 也是单向的
    across
        7
    across  
       2021-04-26 22:38:33 +08:00
    因为 React 只是个 View 层数据流,在 MVC 视角下,你是想把 VC 都放在 ReactComponent 下 这事儿就不应该放 View 层做。
    qiuxuqin
        8
    qiuxuqin  
       2021-04-26 22:40:01 +08:00
    @beizhedenglong vue 的子组件通过 emit 事件,然后父组件接收事件进行处理,这里哪里是单向的了?而且父组件可以直接通过 this.$refs.childComponent 获取到子组件内部的各种变量和方法,这跟单向流动差远了。
    beizhedenglong
        9
    beizhedenglong  
       2021-04-26 22:45:23 +08:00
    @qiuxuqin 按照你这这个逻辑,react 通过 callback 回调传数据和 emit 事件传数据又有啥本质区别?
    agdhole
        10
    agdhole  
       2021-04-27 00:07:40 +08:00
    搜一下 immutable
    Rocketer
        11
    Rocketer  
       2021-04-27 01:33:39 +08:00 via iPhone
    状态管理算是个 hack,初学先别考虑那个。

    React 最重要的基本概念之一就是数据单向流动,只能从父流向子。所以几个组件要想共享数据,就得找个共同的祖先来持有那个数据。

    至于更新状态的函数,你也当数据来用就行了,毕竟 Javascript 里一切皆对象字符串和函数一视同仁。
    mongodb
        12
    mongodb  
       2021-04-27 02:06:53 +08:00
    @Rocketer 那会不会不小心容易写出一种倾向,让最高的父干脆全部负责数据处理得了。。。
    Rocketer
        13
    Rocketer  
       2021-04-27 02:29:11 +08:00 via iPhone
    @mongodb 状态管理本质上就是这么个东西啊。所以说初学先别考虑状态管理,等彻底理解了 React 再看。
    seki
        14
    seki  
       2021-04-27 03:28:35 +08:00
    如果一个组件 props 在更新,自己的 state 也在更新,两部分想归到同一个流里面需要思考很多情况,强行写出来性能可能也会比较差

    把组件分离成专门用于显示的内容的,以及专门用来处理数据的会让逻辑更简单,实现单向数据流也会更少心智负担

    再复杂到一定阶段就会用 context 和 redux / recoil / mobx 之类来把数据处理单独抽出来了
    walpurgis
        15
    walpurgis  
       2021-04-27 08:48:02 +08:00 via Android
    https://zh-hans.reactjs.org/docs/lifting-state-up.html
    数据和逻辑提到父组件是官方推荐做法
    gouflv
        16
    gouflv  
       2021-04-27 09:06:20 +08:00 via iPhone
    1. 如果子组件本身足够复杂,外部可以通过注入类似服务的对象,由服务来间接控制子组件,参考 antd 的 useForm
    2. 简单的子组件,用 useImperativeHandle 暴露 api
    jguo
        17
    jguo  
       2021-04-27 09:08:05 +08:00
    父组件调用子组件的方法意味着强耦合,不符合 react 的思路
    forsigner
        18
    forsigner  
       2021-04-27 10:05:38 +08:00
    可以开始了解 React 之状态管理大乱斗了
    shunia
        19
    shunia  
       2021-04-27 10:20:40 +08:00   1
    这和状态管理有啥关系。。。你只要把 <Component /> 理解成 function Component() {} 就好了。
    AV1
        20
    AV1  
       2021-04-27 10:39:58 +08:00
    父组件没必要去访问子组件内部的状态。
    就像你调用函数的时候,函数外部没必要去获取函数内部的局部变量。
    ccraohng
        21
    ccraohng  
       2021-04-27 10:45:15 +08:00   1
    只关心组件自己的状态维护,如果你只是改变子组件的数据,可以改成受控组件。比较复杂的函数操作,比如 `slider.next()`,通过 ref 暴露就行。
    zhaol
        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}}/>
    }
    }
    zhaol
        23
    zhaol  
       2021-04-27 11:41:22 +08:00
    @zhaol 不建议这样做,但是有时候业务很麻烦,这样做确实会方便.
    towry
        24
    towry  
       2021-04-27 14:52:02 +08:00
    看看 react 的 ref 是怎么实现的。
    DreamTrace
        25
    DreamTrace  
    OP
       2021-04-28 00:04:06 +08:00
    @zhaol 没有看懂......`onReady`似乎是`props`上的一个变量,我应该看哪里?
    zhaol
        26
    zhaol  
       2021-04-28 10:13:32 +08:00
    @DreamTrace 就是 ref 的手动实现,子组件通过 onready 把组件实例抛到父组件用一个变量(subComponentName)保存,这样你就可以通过这个变量调用子组件里面的属性和方法了
    qiuxuqin
        27
    qiuxuqin  
       2021-04-28 11:24:48 +08:00
    @beizhedenglong react 的数据变更函数是通过父组件传到子组件的,在子组件调用 setState 函数实际上是调用父组件的方法,更改父组件的数据,然后传给子组件的那个数据也跟着变了,所以 React 实际上还是数据单向传送的。vue 的 emit 是在子组件通过事件通知父组件,它这个数据变更是通过子组件往上传的,所以是双向流动。
    qiuxuqin
        28
    qiuxuqin  
       2021-04-28 12:04:11 +08:00
    @beizhedenglong 可能我这个解释也不一定准确。我再解释一下:vue 的 v-model 语法其实就算是双向绑定,虽然说它是$props.value 和 emit('change', value )的语法糖。另外如果某个 prop 是个对象,是可以直接修改这个对象的 key 的,这样父组件和子组件也可以的这个对象值也会改变,视图也会更新(当然如果是原始值会报错)。
    beizhedenglong
        29
    beizhedenglong  
       2021-04-28 12:24:23 +08:00 via iPhone
    @qiuxuqin VUE 实现导致的,它不建议你直接改,react 你也可以直接改
    qiuxuqin
        30
    qiuxuqin  
       2021-04-28 19:10:33 +08:00
    @beizhedenglong vue 修改 prop 对象内部的值,它还是响应式的,视图会更新。react 直接修改 prop,视图并不会更新,要用 setState 才会触发视图更新。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5309 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 05:48 PVG 13:48 LAX 22:48 JFK 01:48
    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