反应上下文不保持数据状态

React context not keeping data in state

我正在尝试使用 React 上下文来替换我实际的 Redux。 我想使用此上下文来存储文件数组。 这是一个 nextjs 应用程序,我不知道它会导致一些问题

我有我的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>
    );
}

然后我创建了一个全局上下文,我想在其中初始化我所有的上下文,目前我只有一个文件。

import { FileContextProvider } from "./file";

export default function Context({ children }) {

    return (
        <FileContextProvider>
            {children}
        </FileContextProvider>
    );

}

然后我在_app.js

中添加上下文
import Context from '../context';

function MyApp({ Component, pageProps }) {

  return (
    <Context>
      <Component {...pageProps} />
    </Context>
  )
}

export default MyApp;

在我的一个子组件中我有

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

}

这是我的 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>
    )

}

在我的 UploadBlock 中,当我调用 addLocalFiles 它正在工作时,我的 files 变量包含我的新文件,但是如果我第二次调用 addLocalFiles 前一个FileContextProvider 中不存在文件,console.log('addLocalFiles', files); 返回空数组。

useEffect只是调试用的,每次调用addLocalFiles都会触发,新文件打印好。

我不明白为什么files在回调的时候状态变成空数组addLocalFiles

我只有一个 ContextFileContext.Consumer 我不知道它是否有任何变化

请提供您的组件,以便我们确定您如何使用所需参数调用 addLocalFiles 方法。 我也认为没有必要传播 [...files].concat(newfile) 而不是 files.concat(newfile)

在 React 中,states 依赖于特定的渲染。这意味着,每个渲染器都会有自己的 states。因此,在声明 addLocalFiles 时,特定的 files 状态取决于渲染。

const addLocalFiles = (newFiles, mode) => {
  console.log('addLocalFiles', files);
  setFiles([...files].concat(newFiles)); // state `files` are used
}

我认为问题可能是你用 useCallback.

包装了 addLocalFiles 函数
const onDrop = useCallback(async acceptedFiles => {
  addLocalFiles(acceptedFiles)
}, []) // empty dependency, so it will always referencing same `addLocalFiles` function.

addLocalFiles 永远不会再生成,即使它渲染了。因此,更改后的状态 files 不会应用于 addLocalFiles。 如果你想防止无用的re-render,你应该指定它的依赖,files.

const onDrop = useCallback(async acceptedFiles => {             
  addLocalFiles(acceptedFiles, mode)
}, [addLocalFiles]); // this function is dependent on `files`

或者您可以跳过 useCallback,如果优化不是真的必要的话。我发现了一个 问题可以帮助您更好地理解。