React中Redux与Ref 联合使用时值得关注的一个问题
相信用过React(或者React-Native,以下简称RN)的朋友对Redux
和Ref
这两个概念都很熟悉,前者是开发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;
这样写运行起来没有任何问题:
通过点击‘加加加’实现对数值的+1
处理。
但是假如有这样一个需求:需要在Addition
中读取Redux中的一个数据(当然这个例子没有体现出来),那么就需要connect一下了:
export default connect(mapStateToProps, mapDispatchToProps)(Addition)
嗯,看起来很完美,开开心心得重新运行下,点击,然后....boom( ⊙ o ⊙ )啊!报错了,
不科学啊,说到这里相信大家都能猜出来事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()
获得原对象的Ref
:this.refs.addition.getWrappedInstance().addHandler()
然后重新运行,bingo,一切正常!!!!
源码在这 ,有兴趣的朋友可以拉取代码尝试下。
实测,该方法在React-Native中同样实用,这里就不贴相关代码了