React useRef not updating and rendering new values

Viewed 15970

I'm a beginner React developer and I'm having an issue with this particular code snippet.

Issues:

  1. Not all of the dummy data is being rendered even though I directly copied its value and rendered it as separate children
  2. When I click the add button to make a new input form, it doesn't get added to the render.

I purposely chose to use useRef instead of useState because after the user adds or edit whatever links they want, I want to send keyRef to a NoSQL database; whereas when I used useState() it was giving me stale state issues, where the array with all the links were not updated constantly.

Any suggestions? Please and thank you!

CodeSandbox link: https://codesandbox.io/s/react-hooks-counter-demo-forked-0bjdy?file=/src/index.js

App.js

import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import { links } from './links';

import "./styles.css";

function App() {
  const [loaded, setLoaded] = useState(false);
  const formRef = useRef([]);
  const keyRef = useRef([]);

  if (!loaded) {
    keyRef.current = links;
    links.forEach(link => RenderLinks(link.id));
    setLoaded(true);
  }

  function RenderLinks(id) {
    const formLength = formRef.current.length;

    if (id === null)
    formRef.current = [ ...formRef.current, <AddLink key={formLength} id={formLength} /> ];

    if (id && !formRef.current.find(form => form.props.id === id))
    formRef.current = [ ...formRef.current, <AddLink key={formLength} id={formLength} /> ];
  }

  function AddLink(props) {
    const id = props.id;
    const value = keyRef.current[id] ? keyRef.current[id].link : '';
    const [input, setInput] = useState(value);
    keyRef.current = [
      ...keyRef.current,
      {
        id: id,
        link: '',
      }
    ];
    return <input onChange={e => setInput(e.target.value)} value={input} />
  }

  return (
    <div>
      <button onClick={() => RenderLinks(null)}>add</button>
      {formRef.current ? formRef.current.map(child => child) : null}
    </div>
  )
}

links.js aka dummy data

export const links = [
  {
    id: 0,
    link: "www.zero.com"
  },
  {
    id: 1,
    link: "www.one.com"
  },
  {
    id: 2,
    link: "www.two.com"
  },
  {
    id: 3,
    link: "www.three.com"
  },
  {
    id: 4,
    link: "www.four.com"
  },
  {
    id: 5,
    link: "www.five.com"
  },
  {
    id: 6,
    link: "www.six.com"
  },
  {
    id: 7,
    link: "www.seven.com"
  }
];

3 Answers

From the React useRef() docs:

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render

So even though it is being updated, you will never get a re-render showing the 7th, 8th, etc. links that have been added.

I think you would be better off using useState() hook for your links and rendering those. Perhaps a separate question can identify the stale issues you were seeing.

You can use a state variable to re render all state components, a simple one which I have tried and have succeeded in doing is :

const [, forceUpdate] = useReducer((x) => x + 1, 0)

You can call forceUpdate() when you have added your links and it will function as useState()

You have to use both useState and useRef.

useRef for keeping the latest value and useState for updating the state.

Example :

const showARef = useRef("inline");
const showBRef = useRef("inline");

 const [showA, setshowA] = useState(showARef.current);
 const [showB, setshowB] = useState(showBRef.current);
Related