Actualmente recibo el siguiente error en mi aplicación React:
A los componentes de función no se les pueden dar refs. Los intentos de acceder a esta referencia fallarán. ¿Querías usar React.forwardRef()?
¿Cómo puedo arreglar esto usando forwardRef()?
Mi código es el siguiente:
const Services: FunctionComponent = (): ReactElement => { const servicesRef = useRef(null); return ( <Layout> <ServicesList ref={servicesRef} /> </Layout> ); }; export default Services; const ServicesList: React.FunctionComponent = ({ children }: Props) => { return ( <section className="my-24 md:my-32"> {children && children} </section> ); }; export default ServicesList;Los documentos de reacción explican bien ...
https://reactjs.org/docs/forwarding-refs.html
Envuelva su componente con React.forwardRef y establezca la referencia en el elemento DOM deseado
const FancyButton = React.forwardRef((props,ref)=>( <buttonref={ref} className="FancyButton"> {props.children} ) );
La API forwardRef (junto con el gancho useImperativeHandle ) le permite personalizar cómo y dónde se colocan sus referencias dentro de sus componentes personalizados. Además, forwardRef es la única forma de pasar una referencia a los componentes de su función personalizada.
Primero, es importante comprender que las referencias funcionan de manera diferente en los componentes de clase, los componentes de función y los elementos DOM normales.
De los documentos :
El valor de la referencia difiere según el tipo de nodo:
- Cuando el atributo ref se usa en un elemento HTML, la referencia creada en el constructor con React.createRef() recibe el elemento DOM subyacente como su propiedad actual.
- Cuando el atributo ref se usa en un componente de clase personalizado, el objeto ref recibe la instancia montada del componente como su actual.
- No puede usar el atributo ref en los componentes de la función porque no tienen instancias.
Aquí hay ejemplos de las formas de usar referencias en los diferentes tipos de elementos:
1. La referencia en un elemento DOM le da una referencia al propio nodo DOM: function AutoFocusInput() { const inputRef = useRef(null); // This effect runs only once after the component mounts (like componentDidMount) useEffect(() => { // refs on regular DOM elements (eg the "input" tag) have access to the DOM node inputRef.current.focus(); }, []); return <input ref={inputRef} /> } 2. Ref en un componente de clase nos da acceso a la instancia, con todos sus métodos y campos: class Child extends Component { state = {color: "red"} toggleColor = () => this.setState({color: this.state.color === "red" ? "blue" : "red"}) render() { return <div style={{backgroundColor: this.state.color}}>yo</div> } } class Parent extends Component { childRef = createRef(); handleButtonClicked = () => { // refs on class components: hold the class component instance, // allowing us to call its methods! this.childRef.current.toggleColor(); } render() { return ( <div> <button onClick={this.handleButtonClicked}>toggle color!</button> <Child ref={childRef} /> </div> ); } } 3. Ahora, para finalmente responder a su pregunta. Las referencias no se pueden pasar a los componentes de la función, ¡porque no tienen instancias!La única forma de pasar una referencia a un componente de función es usando forwardRef. Cuando usas forwardRef, simplemente puedes pasar la referencia a un elemento DOM, para que el padre pueda acceder a él como en el ejemplo 1 , o puedes crear un objeto con campos y métodos usando el enlace useImperativeHandle, que sería similar al ejemplo 2 .
3.1 Simplemente pasando una referencia a un elemento DOM: // Only when using forwardRef, the function component receives two arguments, // props and ref (Normally the component only gets the props argument). const RedInput = forwardRef((props, ref) => { // passing the ref to a DOM element, // so that the parent has a reference to the DOM node return <input style={{color: "red"}} {...props} ref={ref} /> }); function AutoFocusInput() { const inputRef = useRef(null); // This effect runs only once after the component mounts (like componentDidMount) useEffect(() => { // ref on function component is forwarded to a regular DOM element, // so now the parent has access to the DOM node including its focus method. // Note that the ref usage is the same as a regular // DOM element, like in example 1! inputRef.current.focus(); }, []); return <RedInput ref={inputRef} /> } 3.2 Adjuntar la referencia principal a un objeto personalizado:Para adjuntar funciones o campos a la referencia, como podrías hacer con la instancia de un componente de clase, necesitas usar el gancho `useImperativeHandle`:
const Child = forwardRef((props, ref) => { const [color, setColor] = useState("red"); // To customize the value that the parent will get in their ref.current: // pass the ref object to useImperativeHandle as the first argument. // Then, whatever will be returned from the callback in the second argument, // will be the value of ref.current. // Here I return an object with the toggleColor method on it, for the parent to use: useImperativeHandle(ref, () => ({ toggleColor: () => setColor(prevColor => prevColor === "red" ? "blue" : "red") })); return <div style={{backgroundColor: color}}>yo</div>; }); class Parent extends Component { childRef = createRef(); handleButtonClicked = () => { // Ref passed to a function component wrapped in forwardRef. // Note that nothing has changed for this Parent component // compared with the class component in example 2! this.childRef.current.toggleColor(); } render() { return ( <div> <button onClick={this.handleButtonClicked}>toggle color!</button> <Child ref={childRef} /> </div> ); } }function Parent(){ const childRef = React.useRef(null) // do something with your childRef React.useEffect(()=> { childREf.current.focus() },[]) return( <div> <Child ref={childRef} /> </div>) } const Child = React.forwardRef((props, ref) => { console.log(ref.current) return( <div tabIndex={1} ref={ref}> child </div) })