How to make element scrolled to bottom in React.js?

In my app I want to make my element always scrolled to bottom after getting new logs.
For some reason my logsRef.current.scrollTop has value of zero all the time. My logs do show on screen and in console. I am not sure why is this not working, I’ve tried to use different approaches using useLyaoutEffect() but nothing made logsRef.current.scrollTop value change, it stayed zero all the time.

//my Logs.jsx component
import { useEffect, useRef } from "react";
import Container from "./UI/Container";
import styles from "./Logs.module.css";

const Logs = ({ logs }) => {
  const logsRef = useRef(null);

  useEffect(() => {
    logsRef.current.scrollTop = logsRef.current.scrollHeight;
    console.log(logs);
    console.log(logsRef.current.scrollTop);
  }, [logs]);

  return (
    <Container className={`${styles.logs} ${styles.container}`}>
      <div ref={logsRef}>
        {" "}
        {logs.map((log, index) => (
          <p key={index}>{log}</p>
        ))}
      </div>
    </Container>
  );
};
export default Logs;

Also, I do render my Logs.jsx in BattlePhase.jsx component where I do my attack logic on click and I save logs using useState() hook.

//parts where i do save my logs in BattlePhase.jsx

const [logs, setLogs] = useState([]);

const attackHandler = () => {
//logs where pokemon on left attacked pokemon on right
setLogs((prevLogs) => [
              ...prevLogs,
              `${pokemonDataOne.name} attacked ${
                pokemonDataTwo.name
              } for ${attack.toFixed(2)} dmg`,
              `${pokemonDataTwo.name} died`,
            ])
}
...
 <Attack className={isActiveArrow}>
          <Button onClick={attackHandler}>Attack!</Button>
        </Attack>

My logs on render and in console.
enter image description here

>Solution :

Slight long shot but it’s possible that the ref is attached to the wrong element. Are you sure the element with the CSS property that makes it scrollable (overflow) isn’t on <Container>?

//my Logs.jsx component
import { useLayoutEffect, useRef } from "react";
import Container from "./UI/Container";
import styles from "./Logs.module.css";

const Logs = ({ logs }) => {
  const logsRef = useRef(null);

  useLayoutEffect(() => {
    logsRef.current.scrollTop = logsRef.current.scrollHeight;
    console.log(logs);
    console.log(logsRef.current.scrollTop);
  }, [logs]);

  return (
    <Container className={`${styles.logs} ${styles.container}`} ref={logsRef}>
      <div>
        {" "}
        {logs.map((log, index) => (
          <p key={index}>{log}</p>
        ))}
      </div>
    </Container>
  );
};
export default Logs;

Also to confirm, you do need useLayoutEffect here.

Leave a Reply