React中Redux与Ref 联合使用时值得关注的一个问题

相信用过React(或者React-Native,以下简称RN)的朋友对ReduxRef这两个概念都很熟悉,前者是开发React或RN比较常用的数据管理框架,当然不仅仅限于React或RN;后者是React提供的专门用于直接操作dom的接口,也可以用来直接调用组件内的方法,当然用法也不仅仅限于这两个。额...似乎有点偏了,因为这两个概念不是本文的重点所以不加赘述,有兴趣的朋友可以到官方文档里一睹庐山真面目。话不多说,下面开始进入正题吧,Let's Go!

接下来我会用一个简单的demo来演示这个问题是如何发生以及如何解决的:
首先新建一个Addition组件,该组件用于显示一个数值,并且可以通过调用Addition组件的addHandler对这个数值进行+1,主要代码如下:

class Addition extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: 0
        }
    }

    addHandler = () => {
        this.setState({
            value: this.state.value + 1
        })
    };

    render() {
        return (
            <div>
                <p>{this.state.value}</p>
            </div>
        )
    }
}

export default Addition

但是我们并没有看到在这个组件里调用了这个方法,所以有经验的朋友可能会猜到这个方法是在其父组件里通过Ref直接调用了这个addHandler方法,代码如下:

class App extends Component {
    clickHandler = () => {
        this.refs.addition.addHandler();
    };

    render() {
        return (
            <Provider store={store}>
                <div className="App">
                    <Addition ref='addition'/>
                    <button onClick={this.clickHandler}>加加加</button>
                </div>
            </Provider>
        );
    }
}

export default App;

这样写运行起来没有任何问题:

React中Redux与Ref 联合使用时值得关注的一个问题

通过点击‘加加加’实现对数值的+1处理。

但是假如有这样一个需求:需要在Addition中读取Redux中的一个数据(当然这个例子没有体现出来),那么就需要connect一下了:
export default connect(mapStateToProps, mapDispatchToProps)(Addition)

嗯,看起来很完美,开开心心得重新运行下,点击,然后....boom( ⊙ o ⊙ )啊!报错了,

React中Redux与Ref 联合使用时值得关注的一个问题

不科学啊,说到这里相信大家都能猜出来事connect过后导致的问题,所以去官方文档翻箱倒柜一下connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
果不其然,原来connect参数有4个,所以可能是遗漏了某个参数吧,所以继续翻...
然后看到这样一段话:

[withRef] (Boolean): If true, stores a ref to the wrapped component instance and makes it available via getWrappedInstance() method. Default value: false

简单理解下,就是说connect过后export出去的不是组件本身,而是经过包装处理的组件,官方称之为wrapped component,所以默认是不将Ref存储到这个包装对象里的(Default value: false),因此只有将withRef这个参数置为true,那么Redux就会将Ref存储到这个包装对象里以供使用了,并且请注意下via getWrappedInstance() method这段话,即便我们将withRef置为true但没有通过getWrappedInstance()获得原对象的Ref(reference)也是不行的。所以说道这里大家也知道解决方案是什么了:

1)connect导出Addition组件时候添加withRef参数:
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(Addition)

2)调用addHandler方法前使用getWrappedInstance()获得原对象的Refthis.refs.addition.getWrappedInstance().addHandler()

然后重新运行,bingo,一切正常!!!!

源码在这 ,有兴趣的朋友可以拉取代码尝试下。

实测,该方法在React-Native中同样实用,这里就不贴相关代码了

相关推荐