javascriptreactjsjsonamazon-s3filetree

Json Array Pushes Data Twice to File Tree with React


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.


Solution

  • 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>
      );
    }