0

When pushing data into a file tree, it pushes 2 sets of the same data and I am not exactly sure why.

I have a simple json array that looks like this:

export const treeDataFile = [{
  type: 'directory',
  name: '2022',
  
}]
  export default treeDataFile;

I am trying to push 4 files I have into it like this by calling this function:


 const addFileToTree = (file,i) => {
      treeDataFile.push({type: i, name: file});
      console.log(treeDataFile)
      setFirstRender(true);   
 };

This is how addFileToTree is being called:

const [files, setFiles] = useState([]);

 //AWS Setup 
 useEffect(() => {
   Amplify.configure({
     Auth:{
       identityPoolId: '',
       region: 'us-east-1',
     },
   
     Storage: {
       AWSS3: {
         bucket:'',
         region: 'ca-central-1',

       }
     }
   });
 },[]);
 
 //gets all files in the bucket
 useEffect(() => {
   Storage.list('').then(files => {
     const s3_files = setFiles(files);
     s3_files.replace("\"eTag\":", "\"perfect\":");
     console.log(s3_files);
     
 
   }).catch(err => {
     console.log(err);
   });
 },[]);

return (    
   <div classname="container">
  <GeistProvider>
    <CssBaseline />
  <Tree  style={{width:"280px", height: "500"}}  value={treeDataFile} onClick={handleShow} />
  </GeistProvider>
       <table>
         <tbody>
           {files.map((file,i) => (
             <tr key={file.key}>
              {addFileToTree(file.key,i)}
             </tr>
           ))}
         </tbody>
         
       </table>
   </div>
 );
};

The result has been this, there should only be 4 items, but it has duplicated it.

enter image description here

Any help would be greatly appreciated.

marcorivera8
  • 217
  • 1
  • 5
  • 13
  • We can be strongly confident that `push` does what it is supposed to do, so that leaves the bug to be with how `addFileToTree` is used. You tagged React so can we see how that is actually being used within the component? – Brian Thompson Apr 04 '22 at 16:21
  • 1
    My magic crystal ball says you're using React's `StrictMode`, which runs component functions twice to ensure you're not doing anything silly in them... such as modifying a global `treeDataFile`. ;-) – AKX Apr 04 '22 at 16:22
  • Where are you calling ```addFileToTree``` can you share the full code ? seems this function is getting called twice ... and also no need to export ```treeDataFile``` twice ... – Anup Apr 04 '22 at 16:24
  • @BrianThompson @ Anup Added it – marcorivera8 Apr 04 '22 at 16:28
  • I still haven't been able to get this to work following AKX's method, does anyone have any ideas of what could be wrong? It seems that the "Component function", does not go through the (files || []).forEach.loop – marcorivera8 Apr 05 '22 at 15:39
  • The code in your "get all files" useEffect makes very little sense. `setFiles` returns undefined, so the rest is basically gibberish. – AKX Apr 05 '22 at 15:51
  • Strange thing is that it works fine with the old code. – marcorivera8 Apr 05 '22 at 15:58
  • I'm pretty sure it spews an error into the console. Yes, as a side effect it does set `files`, but the replace stuff doesn't do anything. – AKX Apr 05 '22 at 16:03

1 Answers1

2

You're mutating the global treeDataFile as a side effect of your component function (and even "worse", as a side effect of a render .map()). Among other things, this would cause treeDataFile to grow larger and larger every time your component is updated.

You're also probably using React's StrictMode, which double-renders your components to begin with to make sure you're not doing anything silly, like you now are.

If your goal is to derive a treeDataFile for that tree component based on the global treeDataFile and files, you can do that with useMemo, which is a hook designed for deriving new data based on other data; in this case your "base" treeDataFile and the files you get. (I assume they're props, since you don't show. They could be state, too, of course.)

I elided the table from this component, since it didn't have any real content based on the original code you had.

EDIT: Based on the augmented code in the question, the expected types for everything become a bit clearer. For one, it's now clear files is an array of AWS Amplify S3 files, and that we're using Geist's Tree component. A full, working example in TypeScript (without AWS Amplify, but with its types) can be found in this CodeSandbox.

const treeDataFile = [{
  type: "directory",
  name: '2022',
}];

export default function App() {
  const [files, setFiles] = React.useState([]);

  React.useEffect(() => {
    // Load files via AWS Amplify.
    Storage.list('').then(setFiles);
  }, []);

  const treeWithFiles = React.useMemo(() => {
    const tree = [...treeDataFiles]; // shallow-copy
    (files || []).forEach((file, i) => {
      tree.push({ type: "file", name: String(file.key) });
    });
    return tree;
  }, [files]);
  return (
    <div className="container">
      <GeistProvider>
        <CssBaseline />
        <Tree style={{ width: "280px", height: "500" }} value={treeWithFiles} />
      </GeistProvider>
    </div>
  );
}
AKX
  • 152,115
  • 15
  • 115
  • 172
  • Thanks, this is very helpful. It did not work right away with your code, but I'll try to use it and your explanation to get it to work on my end. – marcorivera8 Apr 04 '22 at 16:54
  • Sorry I'm kind of new to React, for "files" i am using a state not a hook, would that change your code in any way? – marcorivera8 Apr 04 '22 at 17:24
  • added more details to my question code, it could be because I am using AWS to pull the data. error im getting while using your code: "Error: Objects are not valid as a React child (found: object with keys {key, eTag, lastModified, size}). If you meant to render a collection of children, use an array instead." – marcorivera8 Apr 04 '22 at 18:00
  • I imagine `name: file` should be `name: file.key` in that case. – AKX Apr 04 '22 at 18:02
  • No files seem to show up when doing this. Do you have any other suggestions I can play around with other than useMemo? – marcorivera8 Apr 04 '22 at 19:12
  • @marcorivera8 Please see the edit (and the working example on CodeSandbox). – AKX Apr 05 '22 at 16:02