Initially everything was working fine,I have a component something like.
this
class A extends React.Component {
constructor(props) {
super(props);
this.childRef = null
}
componentDidMount() {
this.childRef = this.refs.b
// now I can call child function like this
this.childRef.calledByParent()
}
render(){
<B ref = "b"/>
}
}
In other file
class B extends React.Component {
calledByParent(){
console.log("i'm called")
}
render(){
<div> hello </div>
}
}
export default B
till here it was working fine but when I do something like this in class B
export default connect(mapStateToProps, mapDispatchToProps)(B)
It is not working. I have imported connect from react-redux
connect()
accepts option
as the forth parameter. In this option parameter you can set flag withRef
to true. After this you can access functions to refs by using getWrappedInstance()
like
class A extends React.Component {
constructor(props) {
super(props);
this.childRef = null
}
componentDidMount() {
this.childRef.getWrappedInstance().calledByParent()
}
render(){
<B ref = {ref => this.childRef = ref}/>
}
}
class B extends React.Component {
calledByParent(){
console.log("i'm called")
}
render(){
<div> hello </div>
}
}
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(B)
I had similar problem but I didn't want to make my APIs dependent on getWrappedInstance()
calls. In fact some components in your class hierarchy may use connect() and access the store and some others are just stateless components that don't need that additional Redux layer.
I have just written a small (maybe a bit hackish) method. Please note, it hasn't been fully tested yet so expect you may need to make some adjustments to get it working in your own scenario.
TypeScript (should be easy to convert to pure JavaScript syntax):
function exposeWrappedMethods(comp: React.ComponentClass<any>, proto?: any): any {
if (!proto) {
if (comp.prototype.constructor.name === 'Connect') {
// Only Redux component created with connect() is supported
proto = comp.prototype.constructor.WrappedComponent.prototype;
} else {
console.warn('Trying to extend an invalid component.');
return comp;
}
}
let prototypeName: string = proto.constructor.name;
if (prototypeName.search(/^React.*Component.*/) < 0 && proto.__proto__) {
for (let propertyName of Object.getOwnPropertyNames(proto)) {
if (!comp.prototype[propertyName]) {
let type: string = typeof proto[propertyName];
if (type === 'function') {
// It's a regular function
comp.prototype[propertyName] = function (...args: any[]) {
return this.wrappedInstance[propertyName](args);
};
} else if (type === 'undefined') {
// It's a property
Object.defineProperty(comp.prototype, propertyName, {
get: function () {
return (this as any).wrappedInstance[propertyName];
},
set: function (value: any) {
(this as any).wrappedInstance[propertyName] = value;
}
});
}
}
}
return exposeWrappedMethods(comp, proto.__proto__);
}
return comp;
}
Use it by simply wrapping your connect()
call with exposeWrappedMethods
. It will add all methods and properties from your own class (and subclasses) but will not overwrite already existing methods (i.e. methods from React.Component base class).
export default exposeWrappedMethods(
connect<any, any, Properties>(
(state: ApplicationState) => state.counter,
CounterState.actionCreators,
null,
{ pure: false, withRef: true } // It requires use of "withRef: true"
)(Counter)) as typeof Counter;
Hope you (or someone else) find it useful.
/Lukasz
Might be a little late but another (better) solution than using refs is to only give control to specific functions of the component.
class A extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.ctrl_B.calledByParent()
}
render(){
<B provideCtrl={ctrl => this.ctrl_B = ctrl} />
}
}
class B extends React.Component {
componentDidMount() {
this.props.provideCtrl({
calledByParent: () => this.calledByParent()
});
}
componentWillUnmount() {
this.props.provideCtrl(null);
}
calledByParent(){
console.log("i'm called")
}
render(){
<div> hello </div>
}
}
export default connect(mapStateToProps, mapDispatchToProps)(B)