React中使用i18next后,如何通过Ref调用子组件方法?
解决withTranslation高阶组件后无法调用子组件方法的问题
这个问题我碰到过好多次啦,核心原因是withTranslation()返回的是一个函数组件,而React的ref默认只能附加到类组件实例或者DOM元素上,不能直接给函数组件用——这就是你看到Function components cannot be given refs错误的根源。下面给你几个实用的解决办法:
方案1:使用i18next官方提供的withRef配置(推荐)
i18next的withTranslation高阶组件支持withRef配置项,开启后会把原始子组件的实例通过getWrappedInstance()方法暴露出来,步骤很简单:
子组件修改:
import { withTranslation } from 'react-i18next'; class Child extends Component { getAlert() { alert('getAlert from Child'); } render() { const { t } = this.props; return <h1>{t('Hello')}</h1>; } } // 开启withRef配置 export default withTranslation({ withRef: true })(Child);
父组件修改:
只需要把调用子组件方法的地方改成通过getWrappedInstance()获取原始实例:
onClick = () => { // 先判断current存在,避免报错 if (this.child.current) { this.child.current.getWrappedInstance().getAlert(); } };
父组件的ref定义和渲染部分完全不用改,还是原来的React.createRef()和<Child ref={this.child} />就行。
方案2:手动用React.forwardRef转发ref
如果你不想依赖withRef配置,也可以自己用React.forwardRef把ref转发到原始子组件上:
子组件修改:
import { withTranslation } from 'react-i18next'; import React from 'react'; class Child extends Component { getAlert() { alert('getAlert from Child'); } // 在组件挂载时把实例传递给forwardedRef componentDidMount() { if (this.props.forwardedRef) { this.props.forwardedRef.current = this; } } // 卸载时清空ref,避免内存泄漏 componentWillUnmount() { if (this.props.forwardedRef) { this.props.forwardedRef.current = null; } } render() { const { t } = this.props; return <h1>{t('Hello')}</h1>; } } // 先用withTranslation包裹子组件 const ChildWithTranslation = withTranslation()(Child); // 再用forwardRef转发ref到子组件的forwardedRef props const ForwardedChild = React.forwardRef((props, ref) => { return <ChildWithTranslation {...props} forwardedRef={ref} />; }); export default ForwardedChild;
父组件修改:
父组件的代码完全不用改,还是原来的this.child.current.getAlert()就可以调用子组件方法了。
方案3:不用ref,通过props传递回调获取方法
如果你想彻底避免使用ref,也可以通过props传递一个回调函数,让子组件把自己的方法实例传递给父组件:
父组件修改:
import Child from 'child'; class Parent extends Component { constructor(props) { super(props); // 存储子组件的方法 this.childGetAlert = null; } onClick = () => { if (this.childGetAlert) { this.childGetAlert(); } }; render() { return ( <div> {/* 传递回调函数给子组件 */} <Child setGetAlert={(method) => this.childGetAlert = method} /> <button onClick={this.onClick}>Click</button> </div> ); } }
子组件修改:
import { withTranslation } from 'react-i18next'; class Child extends Component { componentDidMount() { // 把getAlert方法传递给父组件 this.props.setGetAlert(this.getAlert); } componentWillUnmount() { // 卸载时清空,避免内存泄漏 this.props.setGetAlert(null); } getAlert = () => { alert('getAlert from Child'); }; render() { const { t } = this.props; return <h1>{t('Hello')}</h1>; } } export default withTranslation()(Child);
这三种方案里,方案1是最省心的,因为是i18next官方提供的适配方案,代码改动最少。
内容的提问来源于stack exchange,提问作者CristianR
相关产品推荐
相关产品推荐

