Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use ref in Higher Order Components

I have a Table component that I want ref to be attached to.

Use: Table.js

class Table extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      rows: 1,
      dataLength: props.dataLength,
    }
    this.tableRef = React.createRef(); 
  }

  componentDidUpdate() {
    //using ref
    this.tableRef.current ..... //logic using ref
    this.state.rows ..... //some logic
  }

  render() {
    <TableContainer ref={this.tableRef} />
    <CustomPagination />
  }
}

This works fine, but now my requirement has changed, and I want to reuse the Table component with pagination applied to all the Tables in my App. I have decided to make a HOC withCustomPagination.

Use: withCustomPagination.js HOC

import CustomPagination from 'path/to/file';

const withCustomPagination = tableRef => Component => {
  return class WithCustomPagination extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        rows: 1,
        dataLength: props.dataLength,
      }
    }

    componentDidUpdate() {
      tableRef.current.state ..... //logic using ref, Error for this line
      this.state.rows ..... //some logic
    }

    render() {
      return (
        <Component {...state} />
        <CustomPagination />
      )
    }
  }
}

export default withCustomPagination;

New Table.js:

import withCustomPagination from '/path/to/file';

const ref = React.createRef();

const Table = props => (
  <TableContainer ref={ref} />
);

const WrappedTable = withCustomPagination(ref)(Table);

HOC withCustomPagination returns a class WithCustomPagination that has a componentDidUpdate lifecycle method that uses Table ref in the logic. So I try to pass ref created in Table.js as argument to withCustomPagination, i.e curried with ref and Table stateless component.

This use of ref is wrong and I get error: TypeError: Cannot read property 'state' of null.

I tried using Forwarding Refs, but was unable to implement it.

How do I pass the Table ref to withCustomPagination and be able to use it in HOC?

like image 669
HarshvardhanSharma Avatar asked Dec 30 '18 16:12

HarshvardhanSharma


People also ask

Can we use ref on component?

In order to get a reference to a React component, you can either use this to get the current React component, or you can use a ref to get a reference to a component you own. They work like this: var MyComponent = React. createClass({ handleClick: function() { // Explicitly focus the text input using the raw DOM API.

Can I pass ref as prop?

Regular function or class components don't receive the ref argument, and ref is not available in props either. Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.

How do you pass ref in functional components?

To create a ref in a functional component we use the useRef() hook which returns a mutable object with a . current property set to the initialValue we passed to the hook. This returned object will persist for the full lifetime of the component. Thus, throughout all of its re-rendering and until it unmounts.


2 Answers

In this case you can use useImperativeHandle

It means you have to forward ref and specify which function or object or,... you want to share with ref inside your functional component.

Here is my Hoc example :

import React from 'react';
import { View } from 'react-native';

export function CommonHoc(WrappedComponent) {

    const component = class extends React.Component {

         componentDidMount() {
           this.refs.myComponent.showAlert();

        }

        render() {
            return (
                <>
                    <WrappedComponent
                        ref='myComponent'
                        {...this.state}
                        {...this.props}
                    />
                </>
            );
        }
    };
    return component;
}

and it's my stateless component

const HomeController=(props,ref)=> {

    useImperativeHandle(ref, () => ({

        showAlert() {
            alert("called");
        },
    }));

    return (
        <Text>home</Text>
    );
};
export default CommonHoc(forwardRef(HomeController));
like image 130
zia Avatar answered Oct 01 '22 16:10

zia


Either restructure your code to not use a HOC for this or try using React.forwardRef:

Refs Aren’t Passed Through

While the convention for higher-order components is to pass through all props to the wrapped component, this does not work for refs. That’s because ref is not really a prop — like key, it’s handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component.

The solution for this problem is to use the React.forwardRef API (introduced with React 16.3). Learn more about it in the forwarding refs section.

via Higher-Order Components: Refs Aren’t Passed Through

In the forwarding refs section there are code examples you could use to pass refs down, but trying to yank them up will fail in your case with:

Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.

In a project we took a different approach. There's an EnhancedTable component that handles all of the pagination logic and in itself has the dumb table component and the pagination component. It works pretty well but this means you would have to drill props (or use a store lib like Redux or Mobx) and add new ones that will handle pagination options. This will result in some refactoring of Table uses and you'll have to be more explicit but I would take it as a boon rather than a hindrance.

like image 38
zamber Avatar answered Oct 03 '22 16:10

zamber