Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manage Focus on stateless Components - React

Having a container Component which hold state. it renders a number of stateless components.

I want to get access to all of their DOM nodes, so i can call the focus method on demand.

I am trying the ref approach as it is encouraged by the react documentation.

I'm getting the following error: Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.

What is the recommended way to get around this error? preferably, without extra dom elements wrappers like exra divs. Here is my Current Code:

Container Component - responsible for rendering the stateless components.

import React, { Component } from 'react';
import StatelessComponent from './components/stateless-component.jsx'

class Container extends Component {
    constructor() {
        super()
        this.focusOnFirst = this.focusOnFirst.bind(this)
        this.state = {
            words: [
                'hello',
                'goodbye',
                'see ya'
            ]
        }
    }
    focusOnFirst() {
        this.node1.focus()
    }
    render() {
        return (
            <div>
                {
                    this.state.words.map((word,index)=>{
                        return <StatelessComponent
                        value={word}
                        ref={node => this[`node${index}`] = node}/>
                    })
                }
                <button onClick={this.focusOnFirst}>Focus on First Stateless Component</button>
            </div>
        )
    }
}

Stateless Component - for sake of simplicity, just display a text inside a div.

import React from 'react';
export default function StatelessComponent(props)  {
    return <div>props.value</div>
}
like image 333
LiranC Avatar asked Dec 10 '17 14:12

LiranC


People also ask

How do you focus on component React?

Autofocus using React hooks To focus it when the component renders, we have to use React Hook useEffect, which gets called when particular state variable changes, or we can give an empty array to make sure it renders only once when the component renders.

Should React components be stateless?

As cool as state is, you should always aim to make your components as simple and stateless as possible, so different components can be reused like Lego pieces, even if you don't have immediate plans to reuse a component.

Can stateless components have functions?

Stateless components are those components which don't have any state at all, which means you can't use this. setState inside these components. It is like a normal function with no render method. It has no lifecycle, so it is not possible to use lifecycle methods such as componentDidMount and other hooks.


2 Answers

Stateless (functional) components can't expose refs directly. However, if their internal components can use refs, you can pass a function from the parent (the grandparent) of the stateless component (the parent) as a ref via ref forwarding. Use the function as the ref target of the DOM element. Now the grandparent has direct access to the DOM element ref.

See Exposing DOM Refs to Parent Components in React's documentation.

const StatelessComponent = React.forwardRef((props, ref) => (
  <div>
    <input ref={ref} {...props} />
  </div>
));

class Container extends React.Component { 
  itemRefs = []

  componentDidMount() {
    this.focusOnFirst();
  }

  focusOnFirst = () => this.itemRefs[0].focus()

  inputRef = (ref) => this.itemRefs.push(ref)

  render() {
    return (
      <div>
        <StatelessComponent ref={this.inputRef} />
        <StatelessComponent ref={this.inputRef} />
      </div>
    )
  }
}

ReactDOM.render(
  <Container />,
  demo
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>
like image 122
Ori Drori Avatar answered Oct 04 '22 11:10

Ori Drori


Try this, basically you pass a callback as ref to the stateless components that gets the input instance an adds it to an array owned by the container

class Container extends Component {
    constructor() {
        super()
        this._inputs = [];
        this.focusOnFirst = this.focusOnFirst.bind(this)
        this.state = {
            words: [
                'hello',
                'goodbye',
                'see ya'
            ]
        }
    }
    focusOnFirst() {
        this._inputs[0].focus()
    }

    refCallback(ref) {
        this._inputs.push(ref)
    }

    render() {
        return (
            <div>
                {
                    this.state.words.map((word,index)=>{
                        return <StatelessComponent
                        value={word}
                        refCallback={this.refCallback}/>
                    })
                }
                <button onClick={this.focusOnFirst}>Focus on First Stateless Component</button>
            </div>
        )
    }
}

And the stateless get modified a little too

function StatelessComponent({refCallback, value})  {
    return <input ref={refCallback} value={value}/>
}

Here's a working plunker

like image 23
fethe Avatar answered Oct 04 '22 10:10

fethe