React上传组件传参异常:点击与文件选择时参数不一致
问题:文件选择后UploadAvatar组件参数异常
在React项目中,为DataGrid内的指定用户传递属性至UploadAvatar组件实现文件上传功能时出现异常:
- 点击第三个用户Michael Doe的头像,onClick中的console.log输出正确参数:
mypic.png Michael Doe 3 - 选择文件后,uploadAvatar函数中的console.log输出第一个用户的参数:
app.png John Doe 1
相关代码
模拟数据
export default [ { id: "1", name: "John Doe", avatar: "app.png" }, { id: "2", name: "Anna Doe", avatar: "pic.png" }, { id: "3", name: "Michael Doe", avatar: "mypic.png" } ];
Main组件中列配置
{ field: "avatar", headerName: "Аватар", headerClassName: "home-header", editable: false, align: "center", headerAlign: "center", renderCell: (params) => ( <UploadAvatar avatar={params.value} name={params.row.name} userId={params.row.id} /> ) },
UploadAvatar组件原代码
import React from "react"; import Avatar from "@mui/material/Avatar"; const UploadAvatar = ({ avatar, name, userId }: { avatar: string; name: string; userId: string; }) => { const uploadAvatar = (e: any) => { const file = e.target.files[0]; if (!file) return; console.log(avatar, name, userId); }; return ( <div> {avatar ? ( <Avatar role="button" tabIndex={0} sx={{ marginLeft: "-10px", marginRight: "10px", "&:hover": { cursor: "pointer" } }} alt="avatar" onClick={() => { console.log(avatar, name, userId); document.getElementById("avatar-input")!.click(); }} ></Avatar> ) : ( <Avatar role="button" tabIndex={0} sx={{ marginLeft: "-10px", marginRight: "10px", "&:hover": { cursor: "pointer" } }} alt="avatar" src="" onClick={() => { document.getElementById("avatar-input")!.click(); }} > {name ? name .split(/\s/) .reduce( (response: any, word: any) => (response += word.slice(0, 1)), "" ) : ""} </Avatar> )} <input id="avatar-input" type="file" onChange={uploadAvatar} style={{ display: "none", position: "absolute" }} accept="image/*" /> </div> ); }; export default UploadAvatar;
问题原因
所有UploadAvatar组件实例中的input都使用了相同的id="avatar-input"。当通过document.getElementById("avatar-input")获取元素时,DOM中只会返回第一个匹配该id的元素,因此无论点击哪个用户的头像,触发的都是第一个组件实例的input选择操作,对应的uploadAvatar函数自然也是第一个组件的,参数就变成了第一个用户的信息。
修复方案
方案一:生成唯一ID
为每个input生成唯一的id,结合传入的userId来区分:
import React from "react"; import Avatar from "@mui/material/Avatar"; const UploadAvatar = ({ avatar, name, userId }: { avatar: string; name: string; userId: string; }) => { const inputId = `avatar-input-${userId}`; const uploadAvatar = (e: any) => { const file = e.target.files[0]; if (!file) return; console.log(avatar, name, userId); }; return ( <div> {avatar ? ( <Avatar role="button" tabIndex={0} sx={{ marginLeft: "-10px", marginRight: "10px", "&:hover": { cursor: "pointer" } }} alt="avatar" onClick={() => { console.log(avatar, name, userId); document.getElementById(inputId)!.click(); }} ></Avatar> ) : ( <Avatar role="button" tabIndex={0} sx={{ marginLeft: "-10px", marginRight: "10px", "&:hover": { cursor: "pointer" } }} alt="avatar" src="" onClick={() => { document.getElementById(inputId)!.click(); }} > {name ? name .split(/\s/) .reduce( (response: any, word: any) => (response += word.slice(0, 1)), "" ) : ""} </Avatar> )} <input id={inputId} type="file" onChange={uploadAvatar} style={{ display: "none", position: "absolute" }} accept="image/*" /> </div> ); }; export default UploadAvatar;
方案二:使用React useRef(推荐)
避免直接操作DOM,用React的useRef来获取当前组件的input元素,更符合React的组件化思想:
import React, { useRef } from "react"; import Avatar from "@mui/material/Avatar"; const UploadAvatar = ({ avatar, name, userId }: { avatar: string; name: string; userId: string; }) => { const inputRef = useRef<HTMLInputElement>(null); const uploadAvatar = (e: any) => { const file = e.target.files[0]; if (!file) return; console.log(avatar, name, userId); }; const triggerInputClick = () => { console.log(avatar, name, userId); inputRef.current?.click(); }; return ( <div> {avatar ? ( <Avatar role="button" tabIndex={0} sx={{ marginLeft: "-10px", marginRight: "10px", "&:hover": { cursor: "pointer" } }} alt="avatar" onClick={triggerInputClick} ></Avatar> ) : ( <Avatar role="button" tabIndex={0} sx={{ marginLeft: "-10px", marginRight: "10px", "&:hover": { cursor: "pointer" } }} alt="avatar" src="" onClick={triggerInputClick} > {name ? name .split(/\s/) .reduce( (response: any, word: any) => (response += word.slice(0, 1)), "" ) : ""} </Avatar> )} <input ref={inputRef} type="file" onChange={uploadAvatar} style={{ display: "none", position: "absolute" }} accept="image/*" /> </div> ); }; export default UploadAvatar;
内容的提问来源于stack exchange,提问作者Borislav Stefanov
相关产品推荐
相关产品推荐

