-1

I am trying to convert class component to functional component. but getting this error

Function components cannot have refs. Did you mean to use React.forwardRef()?

working example with class component

https://codesandbox.io/s/highlight-search-results-wqtlm?file=/src/components/Search/List.js

Now I change the list class component to function component

https://codesandbox.io/s/funny-bird-dkfpi?file=/src/components/List.js

my code breaks and give me below error

Function components cannot have refs. Did you mean to use React.forwardRef()?

please suggest how to fix this error

import React, { useEffect, useRef, useState} from 'react';
import User from './User';

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

const List = ({users})=>{
  const [focusedUser, setFocusedUser]  = useState(-1);
  const [scrollIntoView, setScrollIntoView]  = useState(false);
  const userRef = useRef();
  const prev = usePrevious({focusedUser});


  useEffect(()=>{
    if (
        focusedUser !== prev.focusedUser &&
        scrollIntoView
    ) {
      ensureFocusedItemVisible();
    }
  },[focusedUser])

  const ensureFocusedItemVisible =()=> {
     userRef.current &&
    userRef.current.scrollIntoView({
      behaviour: 'smooth',
      block: 'center'
    });
  }

  const handleKeyPress = e => {
    // scroll into view only on keyboard navigation
    setScrollIntoView( true );
    // up arrow
    if (e.keyCode === 38) {
      const fu = focusedUser <= 0 ? 0 : focusedUser - 1;
      setFocusedUser(fu);
    }
    // down arrow
    if (e.keyCode === 40) {
      const currentFocus = focusedUser;
      // if down key is pressed multiple times on last list item, keep last item highlighted
      const fu =
          currentFocus >= users.length - 1
              ? users.length - 1
              : currentFocus + 1;
      setFocusedUser(fu);
    }
  };

 const handleMouseEvent = id => {
    userRef.current && userRef.current.focus();
   setScrollIntoView(false);
   setFocusedUser(parseInt(id))
  };

  const listElements = users.map((user, index) => {
    const focused = index === focusedUser;
    return (
        <User
            divId={index}
            data={user}
            focused={focused}
            ref={focused && userRef}
            handleKeyPress={handleKeyPress}
            handleMouseEvent={handleMouseEvent}
        />
    );
  });

    return <div className="result-list">{listElements}</div>;
}

export default List;
user944513
  • 12,247
  • 49
  • 168
  • 318
  • Does this answer your question? [Function components cannot have refs. Did you mean to use React.forwardRef()?](https://stackoverflow.com/questions/55255746/function-components-cannot-have-refs-did-you-mean-to-use-react-forwardref) – Pedro Fracassi Jul 08 '20 at 10:49

1 Answers1

0

You have 2 errors actually.

The first one is here:

ref={focused && this.userRef}

The second one is here:

const prev = usePrevious({ focusedUser });

useEffect(() => {
  if (focusedUser !== prev.focusedUser && scrollIntoView) {
    ensureFocusedItemVisible();
  }
}, [focusedUser]);

And here is the fix with explanation in comments:

import React, { useEffect, useRef, useState } from "react";
import User from "./User";

const List = ({ users }) => {
  const [focusedUser, setFocusedUser] = useState(-1);
  const [scrollIntoView, setScrollIntoView] = useState(false);
  const userRef = useRef();

  // You don't need to keep track of the previous value.
  // useEffect will be called on first render and whenever one of the values in the dependencies array changes
  // So it will run whenever the focusedUser changes
  useEffect(() => {
    if (scrollIntoView) {
      ensureFocusedItemVisible();
    }
  }, [focusedUser, scrollIntoView]);

  const ensureFocusedItemVisible = () => {
    userRef.current &&
      userRef.current.scrollIntoView({
        behaviour: "smooth",
        block: "center"
      });
  };

  const handleKeyPress = e => {
    // scroll into view only on keyboard navigation
    setScrollIntoView(true);
    // up arrow
    if (e.keyCode === 38) {
      const fu = focusedUser <= 0 ? 0 : focusedUser - 1;
      setFocusedUser(fu);
    }
    // down arrow
    if (e.keyCode === 40) {
      const currentFocus = focusedUser;
      // if down key is pressed multiple times on last list item, keep last item highlighted
      const fu =
        currentFocus >= users.length - 1 ? users.length - 1 : currentFocus + 1;
      setFocusedUser(fu);
    }
  };

  const handleMouseEvent = id => {
    userRef.current && userRef.current.focus();
    setScrollIntoView(false);
    setFocusedUser(parseInt(id));
  };

  const listElements = users.map((user, index) => {
    const focused = index === focusedUser;
    return (
      <User
        key={index}
        divId={index}
        data={user}
        focused={focused}
        // if it isn't focused pass null
        // the ref should be a function or a ref object or null
        ref={focused ? userRef : null}
        handleKeyPress={handleKeyPress}
        handleMouseEvent={handleMouseEvent}
      />
    );
  });

  return <div className="result-list">{listElements}</div>;
};

export default List;
Ahmed Mokhtar
  • 2,388
  • 1
  • 9
  • 19