React 重复组件更新错误状态:钩子

React Duplicate components updating wrong state: hooks

我是反应新手,才用了几天,所以如果这是一个愚蠢的问题,请原谅我。

我有一个文件输入组件和一个图像缩略图组件,我使用两个重复的文件输入组件来更新两个不同的状态,然后在两个不同的缩略图组件中显示来自不同状态的图像。我在所有组件上设置了唯一键,但只有 Dom 中第一个组件的状态被更新。当我使用第二个文件输入添加图像时,它会更新属于第一个文件输入的状态。

我已经尝试寻找解决方案,所有解决方案都声明要使用唯一密钥,我认为我做得很好。

let [certification, setCertification] = useState(null)
let [photoId, setPhotoId] = useState(null)

let handleUpdateCertificate = (e) =>{
    let file = e.target.files[0]
    console.log(file)
    let path = URL.createObjectURL(file)

    let newCertificate = {
        'file': file,
        'path' : path
    }

    setCertification(newCertificate)
}

let handleUpdatePhotoId = (e) => {
    let file = e.target.photoidinput.files[0]
    let path = URL.createObjectURL(file)

    let newPhotoID = {
        'file': file,
        'path' : path
    }

    setPhotoId(newPhotoID)

}

我的returnhtml是:

     <div className='justify-content-center margin-20' key='certificate-wrapper'>
        <ImgThumbnail key={'certificate'} name={'certificate'} image= 
             {certification?.path} wrapperClass={'justify-content-center margin-20'}/>
      </div>
      <div className='justify-content-center margin-20'>
         <FileInput key={'certificateinput'} name={'certificateinput'} labelText={<p 
                    className='text-paragraph edit-btn-text'>Add Certificate</p>} 
                     onChange={handleUpdateCertificate}
                     classWrapper={'edit-profile-responsive-btn-wrapper'}/>
      </div>
  <div className='justify-content-center margin-20 ' key='photo-Id'>
       <ImgThumbnail key={'photoid'} name={'photoId'} image={photoId?.path} 
                  wrapperClass={'justify-content-center margin-20'}/>
  </div>
                            
  <div className='justify-content-center margin-20' key='photo-id-input-wrapper'>
      <FileInput key={'photoidinput'} name={'photoidinput'} labelText={<p 
                  className='text-paragraph edit-btn-text'>Add Photo ID</p>} 
                  onChange={handleUpdatePhotoId}
                  classWrapper={'edit-profile-responsive-btn-wrapper'}/>
   </div>

好的,我会给你一些提示,然后给你一个工作示例:

  • 如果您正在编写这样的 JSX 元素,则不需要设置 key 属性,只有当您呈现来自一个数组,防止数组更新时无用re-rendering。

  • 使用 const 而不是 let 当变量是静态时,有一个关于它的 lint 规则!

  • 尝试使用 DRY,你的更新处理程序共享很多逻辑,如果你要添加更多的输入,那将是所有代码重复。

现在代码:

import React, { useState } from 'react';
import './style.css';

export default function App() {
  const [certification, setCertification] = useState(null);
  const [photoId, setPhotoId] = useState(null);

  const updateData = (file, cb) => {
    const path = URL.createObjectURL(file);
    const data = {
      file: file,
      path: path,
    };
    cb(data);
  };

  const handleUpdateCertificate = (e) => {
    updateData(e.target.files[0], setCertification);
  };

  const handleUpdatePhotoId = (e) => {
    updateData(e.target.files[0], setPhotoId);
  };

  return (
    <div>
      {certification && (
        <div className="justify-content-center margin-20">
          <ImgThumbnail
            name={'certificate'}
            image={certification?.path}
            wrapperClass={'justify-content-center margin-20'}
          />
        </div>
      )}
      <div className="justify-content-center margin-20">
        <FileInput
          id="certificate"
          name={'certificateinput'}
          labelText={
            <p className="text-paragraph edit-btn-text">Add Certificate</p>
          }
          onChange={handleUpdateCertificate}
          classWrapper={'edit-profile-responsive-btn-wrapper'}
        />
      </div>
      {photoId && (
        <div className="justify-content-center margin-20 " key="photo-Id">
          <ImgThumbnail
            name={'photoId'}
            image={photoId?.path}
            wrapperClass={'justify-content-center margin-20'}
          />
        </div>
      )}

      <div
        className="justify-content-center margin-20"
        key="photo-id-input-wrapper"
      >
        <FileInput
          id="photo"
          name={'photoidinput'}
          labelText={
            <p className="text-paragraph edit-btn-text">Add Photo ID</p>
          }
          onChange={handleUpdatePhotoId}
          classWrapper={'edit-profile-responsive-btn-wrapper'}
        />
      </div>
    </div>
  );
}

const FileInput = ({ id, labelText, ...props }) => (
  <label htmlFor={id}>
    {labelText}
    <input id={id} style={{ display: 'none' }} type="file" {...props} />
  </label>
);

const ImgThumbnail = ({ name, image }) => (
  <div>
    <img style={{ width: '100px', height: '100px' }} src={image} alt={name} />
  </div>
);

这个例子是正确的,你可能在 FileInput 组件中做错了,记住标签必须有一个 htmlFor 属性使用要触发的 input 元素的 id

现在,可以优化此代码并使其更加 React 风格,因为您将来可能会有更多的文件输入,让我们看看如何通过创建可重用组件来优化它并正确组合它们:

import React, { useState } from 'react';
import './style.css';

/* INPUTS IMAGE TYPES */

const inputs = [
  { type: 'photo', name: 'photo', label: 'Photo' },
  { type: 'certificate', name: 'certificate', label: 'Certificate' },
  { type: 'anotherType', name: 'anotherName', label: 'Another Input' },
];

export default function App() {
  return (
    <div>
      {inputs.map((data) => (
        <ImagePreviewer key={data.type} data={data} />
      ))}
    </div>
  );
}

const FileInput = ({ id, labelText, ...props }) => (
  <label htmlFor={id}>
    {labelText}
    <input id={id} style={{ display: 'none' }} type="file" {...props} />
  </label>
);

const ImgThumbnail = ({ name, image }) => (
  <div>
    <img src={image} alt={name} />
  </div>
);

const ImagePreviewer = ({ data: { type, name, label } }) => {
  const [image, setImage] = useState(null);

  const updateData = (file, cb) => {
    const path = URL.createObjectURL(file);
    const data = {
      file: file,
      path: path,
    };
    cb(data);
  };

  const handleUpdate = (e) => {
    updateData(e.target.files[0], setImage);
  };

  return (
    <div>
      {image && (
        <div>
          <ImgThumbnail name={'name'} image={image?.path} />
        </div>
      )}
      <div>
        <FileInput
          id={name}
          name={name}
          labelText={<p>Add {label}</p>}
          onChange={handleUpdate}
        />
      </div>
    </div>
  );
};

一个工作演示 HERE