Reputation: 10089
I am trying to use React context to replace my actual Redux. I want to use this context to store an array of files. This is a nextjs app, I do not know it can cause some issues
I have my FileContextProvider
import { createContext, useState, useEffect } from "react";
export const FileContext = createContext({});
export function FileContextProvider({ children }) {
const [files, setFiles] = useState([]);
useEffect(() => {
console.log('files', files);
}, [files])
const addLocalFiles = (newFiles, mode) => {
console.log('addLocalFiles', files);
setFiles([...files].concat(newFiles));
}
return (
<FileContext.Provider value={{
files,
addLocalFiles
}} >
{children}
</FileContext.Provider>
);
}
Then I created a global context where I want to initialize all my context, at the moment I only have the file one.
import { FileContextProvider } from "./file";
export default function Context({ children }) {
return (
<FileContextProvider>
{children}
</FileContextProvider>
);
}
Then I add the context in _app.js
import Context from '../context';
function MyApp({ Component, pageProps }) {
return (
<Context>
<Component {...pageProps} />
</Context>
)
}
export default MyApp;
In one of my child component I have
import { FileContext } from '../../context/file';
export default function TopBlock({ type, format }) {
return (
<div className="col-12 col-lg-8 offset-lg-2">
<FileContext.Consumer>
{({ files, addLocalFiles, removeFile, updateFile }) => (
<UploadBlock files={files} addLocalFiles={addLocalFiles} />
)}
</FileContext.Consumer>
</div>
);
}
Here my UploadBlock
import React, { useCallback, useRef } from 'react';
import { useDropzone } from 'react-dropzone';
export default function UploadBlock({ files, addLocalFiles }) {
const dropzoneParent = useRef(null);
function MyDropzone() {
const onDrop = useCallback(async acceptedFiles => {
addLocalFiles(acceptedFiles)
}, [])
const dropOptions = { onDrop }
const { getRootProps, getInputProps, isDragActive } = useDropzone(dropOptions)
return (
<div {...getRootProps()} ref={dropzoneParent}>
<input {...getInputProps()} />
<div>Drop Here</div>
</div>
)
}
return (
<div>
{MyDropzone()}
</div>
)
}
In my UploadBlock
when I call addLocalFiles
it's working, I have my files
variable containing my new files but if I call a second time addLocalFiles
the previous files does not exist in FileContextProvider
, console.log('addLocalFiles', files);
is returning an empty array.
The useEffect
is only to debug, it is triggered each time addLocalFiles
is called, and new files are well print.
I do not understand why files
from the state became an empty array when I am calling back addLocalFiles
I have only one instance of Context
and FileContext.Consumer
I do not know if it makes any changes
Upvotes: 0
Views: 2210
Reputation: 556
In React, states
are dependent on specific render. It means, every render will have its own states
. So, by the time addLocalFiles
are declared, specific files
states are dependent on the render.
const addLocalFiles = (newFiles, mode) => {
console.log('addLocalFiles', files);
setFiles([...files].concat(newFiles)); // state `files` are used
}
I think the problem could be that you are wrapping addLocalFiles
function with useCallback
.
const onDrop = useCallback(async acceptedFiles => {
addLocalFiles(acceptedFiles)
}, []) // empty dependency, so it will always referencing same `addLocalFiles` function.
addLocalFiles
will never be generated again, even if it renders. So, changed states files
are not going to be applied in addLocalFiles
.
If you want to prevent useless re-render, you should specifying its dependency, files
.
const onDrop = useCallback(async acceptedFiles => {
addLocalFiles(acceptedFiles, mode)
}, [addLocalFiles]); // this function is dependent on `files`
Or you can just skip useCallback
, if optimization is not really necessary. I found a StackOverflow question that could help you better understading.
Upvotes: 1
Reputation: 1
please provide your component so we can be sure how u call the addLocalFiles
method with the required parameter.
i also think there is no need for spreading [...files].concat(newfile) instead of files.concat(newfile)
Upvotes: 0