8

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?

3 Answers 3

3

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)); 
Sign up to request clarification or add additional context in comments.

1 Comment

Can you please provide an updated answer for new React.createRef() based solution.
1

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.

Comments

0

I was able to solve a simmilar issue that brought me to this thread without using forwardRef or useImperativeHandle.

By creating the ref at a higher level, and passign it down into the component and sub components that I needed to act on with the ref.

/** Parent Component has access to ref and functions that act on ref **/ import { useRef } from 'react'; const formRef = useRef(); // ref will have dom elements need accessing const onClickFunction=()=>{ //sample function acts on ref var inputs = formRef.current.querySelectorAll('input') /* Act on ref here via onClick function, etc has access to dom elements in child component and childs child components */ }; return( <ComponentGetsAttachedRef formRef={formRef} /> //^ref sent down to component and its children <ComponentNeedingRef onClickFunction={onClickFunction}/> //^function with access to ref sent down to component ) /** Child component needs to act on ref**/ export const ComponentNeedingRef = ({ onClickFunction}) =>{ return( <button onClick={onClickFunction}> ) } /* Child component recieves ref and passes it down */ export const ComponentGetsAttachedRef = ({ formRef}) =>{ //ref comes in as prop gets attached to props or utilized internally return ( <ChildsChildComponent formRef={formRef}/> //sub component passed ref down ) } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.