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>
}
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.
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.
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.
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>
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With