0
votes

I am getting an error in my console,

my JSON is here https://dev.justinblayney.com/wp-content/uploads/2020/12/main-menu.json_.zip

Warning: Each child in a list should have a unique "key" prop.

What displays on my page is (So they are all unique, so WTF react)

KEY: 2429 KEY: 2430 KEY: 3859 KEY: 2421 KEY: 2802 KEY: 2428

On a side note, I'm discovering that using a function is a terrible way to get a JSON file, I also get memory leak warnings and every tutorial I see online uses classes or axios

Check the render method of MyRoutes. See https://reactjs.org/link/warning-keys for more information. MyRoutes@http://localhost:3000/react-wordpress-headless/static/js/main.chunk.js:63:81 div Router@http://localhost:3000/react-wordpress-headless/static/js/0.chunk.js:35747:30 BrowserRouter@http://localhost:3000/react-wordpress-headless/static/js/0.chunk.js:35367:35 App@http://localhost:3000/react-wordpress-headless/static/js/main.chunk.js:94:1

    function MyRoutes() {
    
    const [myrt, setMyrt] = useState([]); 
  
    
    useEffect(() => {
        fetch("main-menu.json" ,{
      headers : { 
        'Content-Type': 'application/json',
        'Accept': 'application/json'
       }
    })
        .then(res => res.json())
    
        .then(json =>{
             setMyrt(json.items)}
              )
         });
        
    
    return (
        <>
            {Object.keys(myrt).map((ky, idx)=> (
            <>
             <h2>KEY: {myrt[ky].ID} </h2>
        <Route exact path={`/${myrt[ky].slug}`} component={Page} key={myrt[ky].ID}  /></>
            ))} 
        </>
  
    );
}
4
Your key isn't on the actual child, which is the fragment.jonrsharpe
how do i fix that?Justin Blayney
jonrsharpe is correct. Also the memory leak you are probably seeing is that you have passed no dependencies into useEffect so its running on every single render.topched

4 Answers

3
votes

The key prop should be defined on the first most element, in your case it is React.Fragment.

function MyRoutes() {
  const [myrt, setMyrt] = useState([]);

  useEffect(() => {
    fetch('main-menu.json', {
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
      },
    })
      .then((res) => res.json())

      .then((json) => {
        setMyrt(json.items);
      });
  });

  return (
    <>
      {Object.keys(myrt).map((ky, idx) => (
        <React.Fragment key={ky}>
          // ------------^
          <h2>KEY: {myrt[ky].ID} </h2>
          <Route exact path={`/${myrt[ky].slug}`} component={Page} key={myrt[ky].ID} />
        </React.Fragment>
      ))}
    </>
  );
}
1
votes

The key property is needed on the first-level of the list child element, thus I think in your example the React fragment within your .map needs to have this key.

1
votes

You need the key on the top component/tag that it's rendering on the map. Example: if you use a div

<div key={myrt[ky].ID} >
    <h2>KEY: {myrt[ky].ID} </h2>
    <Route exact path={`/${myrt[ky].slug}`} component={Page} />
</div>
1
votes

For your memory leak you are calling the function get the JSON file infinite times because you have not specified any dependencies in the useEffect. So you set the data, it rerenders, then fetches again forever

    useEffect(() => {
        fetch("main-menu.json" ,{
      headers : { 
        'Content-Type': 'application/json',
        'Accept': 'application/json'
       }
    })
        .then(res => res.json())
    
        .then(json =>{
             setMyrt(json.items)}
              )
         }, [] /* Adding this only runs this once */);