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.
Any help would be greatly appreciated.
Advertisement
Answer
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> ); }