首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaoming1992
V2EX  ›  React

react hooks 如何实现父组件调用子组件的方法?

  •  
  •   xiaoming1992 · 30 天前 · 892 次点击

    两点前提:

    1. 函数式组件, 使用 react hooks
    2. 子组件导出方法, 供父组件在恰当的时机(想什么时候就什么时候)调用

    我唯一想到的方法是使用 ref, 如下, 但是这也太 TM 丑了, 求助大佬们

    type ChildFunc = () => number
    
    // eslint-disable-next-line react/display-name
    const Child = React.forwardRef((props, ref) => {
    
      // eslint-disable-next-line no-param-reassign
      (ref as React.MutableRefObject<ChildFunc | undefined>).current = () => 5
    
      return <>I am child</>
    })
    
    // Parent
    function Parent() {
      const funcRef = useRef<ChildFunc>()
    
      const theRightTime = new Promise((resolve) => {
        setTimeout(resolve, 1000)
      })
    
      useEffect(() => {
        theRightTime.then(() => {
          if (funcRef.current) {
            console.log(funcRef.current())
          }
        })
      }, [funcRef, theRightTime])
    
      return <Child ref={funcRef} />
    }
    

    顺便提一嘴, react 官网顶部有个"条幅" -- "黑人的命也是命。 支持公正司法倡议。" -- 但是各位可以切换语言看一下, 好像只有简体中文和英文有, 繁体中文、日文等都没有(那么多语言, 我也没一个一个翻, 但是我看了的好几个都没有)

    第 1 条附言  ·  30 天前
    讲一下问题的起因吧,我正在做一个翻纸牌的小游戏。
    有一大批纸牌,**我认为**每张纸牌应该有一定的的智慧和自主权决定自己是正面还是反面,从原则上来讲,我不想干涉它。我的做法是当子组件切换状态的时候通知父组件。

    可是从业务上讲,我又需要有能力(vip 功能),控制每一张牌的翻面。

    有朋友提到单向数据流,确实,如果直接由父组件指定每个子组件的状态,那这个问题根本就不是问题,可是因为是自己的玩具,所以我想任性一把(可是实力不允许😂)
    16 条回复    2020-06-04 17:31:23 +08:00
    fuermosi777
        1
    fuermosi777   30 天前
    要不子组件还是 class 吧
    crs0910
        2
    crs0910   30 天前 via iPhone
    为什么不把 theRightTime 作为 context 给子组件用呢
    crs0910
        3
    crs0910   30 天前 via iPhone
    这个运动的声明好像是 Django rest api 的作者发起的,他们是直接把官方文档给换成了支持声明。react 只挂个横幅也算是很大支持了。
    ps4512
        4
    ps4512   30 天前 via iPhone
    callback
    dartabe
        5
    dartabe   30 天前
    react 不是应该单项数据流吗 你这个要求是不是应该把 function 直接放到父组件或者 util ?

    我是菜鸟哈
    WittBulter
        6
    WittBulter   30 天前   ❤️ 1
    React 有一个 useImperativeHandle 允许你用 hooks 的方式向外部暴露指定的实例属性或方法。

    给你写了一个在线示例可以参考: https://codesandbox.io/s/invoker-child-methdos-gtwch?file=/src/app.js
    ChefIsAwesome
        7
    ChefIsAwesome   30 天前
    传 ref 本质上就是传了个 onLoad 函数给子组件。子组件 mount 之后调这个函数,并且把它自己传进去。
    你这个子组件提供方法的问题是一个道理。
    xiaoming1992
        8
    xiaoming1992   30 天前
    @fuermosi777 我其他功能和样式都写完了,不想改了啊 T.T
    @crs0910 这个 theRightTime 只是表明,在恰当的时机,由父组件触发而已,本质上还是说父组件需要在恰当的时机调用子组件的方法啊
    @ps4512 跟 callback 应该没什么关系吧
    @dartabe 确实放到父组件就没有这些问题了,只是有些私心,可以看看我 append 的内容
    @WittBulter 这个 hook 我倒是确实不知道,涨姿势了,感谢(但是说实话,这样还是不够"优雅"啊...)
    @ChefIsAwesome 不管是 ref 也好,全局变量也好,目前来看虽然勉强能实现,但是都有些丑,6L 的应该算是目前最佳的了
    xiaoming1992
        9
    xiaoming1992   30 天前
    @WittBulter 不对,仔细看了一下,应该还是比较优雅的,比较完美贴合我这个需求的
    hantsy
        10
    hantsy   30 天前
    BlackLivesmatter 现在闹得比疫情还严重,不光是美国,欧洲也开始打杂抢了。
    KuroNekoFan
        11
    KuroNekoFan   29 天前
    方法很多比如
    父组件改子组件的 props 然后子组件把这个 props 作为 dependency 来触发行为
    LeoooY
        12
    LeoooY   29 天前
    我一般是把子组件封装成一个 useXXX(),然后 return dom 和需要调用的方法
    const useChild=()=>{
    return {
    }
    }
    LeoooY
        13
    LeoooY   29 天前   ❤️ 1
    我一般是把子组件封装成一个 useXXX(),然后 return dom 和需要调用的方法
    const useChild=()=>{
    return {
    dom: <child />
    methods:{}
    }
    }
    xiaoming1992
        14
    xiaoming1992   29 天前 via Android
    @LeoooY 这倒是一个好方法,我当时怎么没想到呢。。。我已经把原来由子组件控制的属性大部分移到父组件了。。。
    xiaoming1992
        15
    xiaoming1992   29 天前 via Android
    @KuroNekoFan 可能不太行吧?之所以说希望子组件暴露出函数,就是因为函数用起来方便且语义明晰,用属性可能在这两方面不太合适吧?
    KuroNekoFan
        16
    KuroNekoFan   29 天前
    @xiaoming1992 所谓'子组件暴露出函数',react 是不希望开发者这样做的
    https://zh-hans.reactjs.org/docs/hooks-reference.html#useimperativehandle 官方文档也说了,应当避免
    除了我说的用 props 控制,还可以用事件,状态管理之类的
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2813 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 11:29 · PVG 19:29 · LAX 04:29 · JFK 07:29
    ♥ Do have faith in what you're doing.