Textfield 在 React 的 onChange 后失去焦点。

huangapple go评论77阅读模式
英文:

Textfield loses focus after one key in react onChange

问题

以下是您提供的代码的翻译部分:

当我在文本字段中输入键时,它们失去焦点。
我已经发现是因为onChange,但我看不到任何问题

> onChange(e.target.value)

我在codesandbox.io中创建了一个示例 https://codesandbox.io/s/gallant-dust-3wdzhh?file=/src/App.js

以下是文本字段的代码(来自该sandbox):

import * as React from "react";
import { styled } from "@mui/material/styles";
import Box from "@mui/material/Box";
import {
  TextField,
  Input,
  inputBaseClasses,
  FormControl,
  InputLabel
} from "@mui/material";
import { BiShow, BiHide } from "react-icons/bi";
import { useState } from "react";

const ValidationTextField = styled(TextField)({
  "& input:valid + fieldset": {
    borderColor: "green",
    borderWidth: 2
  },
  "& input:invalid + fieldset": {
    borderColor: "red",
    borderWidth: 2
  },
  "&:hover:valid + fieldset": {
    borderColor: "yellow"
  },
  "& input:valid:focus + fieldset": {
    borderLeftWidth: 6,
    borderColor: "purple",
    color: "pink",
    padding: "4px !important"
  },
  "& .MuiOutlinedInput-root": {
    "&:hover fieldset": {
      borderColor: "yellow",
      color: "orange"
    }
  }
});

const StyledInput = styled(Input)({
  borderRadius: 4,
  border: "2px solid blue",
  padding: 4,
  [`&.${inputBaseClasses.multiline}`]: {
    height: "auto",
    border: "2px solid red"
  }
});

export default function Text({
  errors,
  label,
  rows,
  type,
  defaultValue,
  required,
  onChange,
  setValue,
  name
}) {
  const [showPass, setShowPass] = useState(false);

  /*const handleChange = (e) => {
    const eTarget = e.target.value;
    const eName = e.target.name;
    setValue(eName, eTarget);
  };*/

  const PassWordIcon = () =>
    showPass ? (
      <BiHide size={40} onClick={() => setShowPass(false)} />
    ) : (
      <BiShow size={40} onClick={() => setShowPass(true)} />
    );

  const Multiline = () => {
    return (
      <FormControl variant="outlined">
        <InputLabel>{label}</InputLabel>
        <StyledInput
          sx={{
            "&:hover": {
              border: "2px solid yellow"
            },
            "&.Mui-focused": {
              borderColor: "purple",
              color: "pink",
              padding: "4px !important"
            }
          }}
          onChange={(e) => onChange(e.target.value)}
          disableUnderline
          multiline
          rows={rows}
        />
      </FormControl>
    );
  };

  const Password = () => {
    return (
      <Box>
        <ValidationTextField
          label={label}
          required={required}
          error={errors}
          onChange={(e) => onChange(e.target.value)}
          type={showPass ? "text" : "password"}
          sx={{
            input: {
              color: "red",
              "&::placeholder": {
                color: "darkgreen",
                "&:hover fieldset": {
                  color: "orange"
                }
              }
            },
            label: {
              color: "pink"
            }
          }}
          variant="outlined"
          defaultValue={defaultValue}
        />
        <PassWordIcon />
      </Box>
    );
  };

  const TextInput = () => {
    return (
      <ValidationTextField
        label={label}
        required={required}
        type={type}
        onChange={(e) => onChange(e.target.value)}
        sx={{
          input: {
            color: "red",
            "&::placeholder": {
              color: "darkgreen",
              "&:hover fieldset": {
                color: "orange"
              }
            }
          },
          label: {
            color: "pink"
          }
        }}
        variant="outlined"
        defaultValue={defaultValue}
      />
    );
  };

  const inputType = (type) => {
    switch (type) {
      case "multiline":
        return <Multiline />;
      case "password":
        return <Password />;
      default:
        return <TextInput />;
    }
  };

  return <Box>{inputType(type)}</Box>;
}

请注意,这是您提供的代码的中文翻译。如果您需要进一步的帮助或有其他问题,请随时提问。

英文:

When I enter a key in my textFields they lose focus.
I have found out that it's because of the onChange but I don't see anything wrong with it

> onChange(e.target.value)

I have made a sample in codesandbox.io https://codesandbox.io/s/gallant-dust-3wdzhh?file=/src/App.js

Here's the code (from that sandbox) for the text fields:

import * as React from &quot;react&quot;;
import { styled } from &quot;@mui/material/styles&quot;;
import Box from &quot;@mui/material/Box&quot;;
import {
TextField,
Input,
inputBaseClasses,
FormControl,
InputLabel
} from &quot;@mui/material&quot;;
import { BiShow, BiHide } from &quot;react-icons/bi&quot;;
import { useState } from &quot;react&quot;;
const ValidationTextField = styled(TextField)({
&quot;&amp; input:valid + fieldset&quot;: {
borderColor: &quot;green&quot;,
borderWidth: 2
},
&quot;&amp; input:invalid + fieldset&quot;: {
borderColor: &quot;red&quot;,
borderWidth: 2
},
&quot;&amp;:hover:valid + fieldset&quot;: {
borderColor: &quot;yellow&quot;
},
&quot;&amp; input:valid:focus + fieldset&quot;: {
borderLeftWidth: 6,
borderColor: &quot;purple&quot;,
color: &quot;pink&quot;,
padding: &quot;4px !important&quot;
},
&quot;&amp; .MuiOutlinedInput-root&quot;: {
&quot;&amp;:hover fieldset&quot;: {
borderColor: &quot;yellow&quot;,
color: &quot;orange&quot;
}
}
});
const StyledInput = styled(Input)({
borderRadius: 4,
border: &quot;2px solid blue&quot;,
padding: 4,
[`&amp;.${inputBaseClasses.multiline}`]: {
height: &quot;auto&quot;,
border: &quot;2px solid red&quot;
}
});
export default function Text({
errors,
label,
rows,
type,
defaultValue,
required,
onChange,
setValue,
name
}) {
const [showPass, setShowPass] = useState(false);
/*const handleChange = (e) =&gt; {
const eTarget = e.target.value;
const eName = e.target.name;
setValue(eName, eTarget);
};*/
const PassWordIcon = () =&gt;
showPass ? (
&lt;BiHide size={40} onClick={() =&gt; setShowPass(false)} /&gt;
) : (
&lt;BiShow size={40} onClick={() =&gt; setShowPass(true)} /&gt;
);
const Multiline = () =&gt; {
return (
&lt;FormControl variant=&quot;outlined&quot;&gt;
&lt;InputLabel&gt;{label}&lt;/InputLabel&gt;
&lt;StyledInput
sx={{
&quot;&amp;:hover&quot;: {
border: &quot;2px solid yellow&quot;
},
&quot;&amp;.Mui-focused&quot;: {
borderColor: &quot;purple&quot;,
color: &quot;pink&quot;,
padding: &quot;4px !important&quot;
}
}}
onChange={(e) =&gt; onChange(e.target.value)}
disableUnderline
multiline
rows={rows}
/&gt;
&lt;/FormControl&gt;
);
};
const Password = () =&gt; {
return (
&lt;Box&gt;
&lt;ValidationTextField
label={label}
required={required}
error={errors}
onChange={(e) =&gt; onChange(e.target.value)}
type={showPass ? &quot;text&quot; : &quot;password&quot;}
sx={{
input: {
color: &quot;red&quot;,
&quot;&amp;::placeholder&quot;: {
color: &quot;darkgreen&quot;,
&quot;&amp;:hover fieldset&quot;: {
color: &quot;orange&quot;
}
}
},
label: {
color: &quot;pink&quot;
}
}}
variant=&quot;outlined&quot;
defaultValue={defaultValue}
/&gt;
&lt;PassWordIcon /&gt;
&lt;/Box&gt;
);
};
const TextInput = () =&gt; {
return (
&lt;ValidationTextField
label={label}
required={required}
type={type}
onChange={(e) =&gt; onChange(e.target.value)}
sx={{
input: {
color: &quot;red&quot;,
&quot;&amp;::placeholder&quot;: {
color: &quot;darkgreen&quot;,
&quot;&amp;:hover fieldset&quot;: {
color: &quot;orange&quot;
}
}
},
label: {
color: &quot;pink&quot;
}
}}
variant=&quot;outlined&quot;
defaultValue={defaultValue}
/&gt;
);
};
const inputType = (type) =&gt; {
switch (type) {
case &quot;multiline&quot;:
return &lt;Multiline /&gt;;
case &quot;password&quot;:
return &lt;Password /&gt;;
default:
return &lt;TextInput /&gt;;
}
};
return &lt;Box&gt;{inputType(type)}&lt;/Box&gt;;
}

答案1

得分: 0

你正在Text组件内部定义PasswordIconMultilinePasswordTextInput组件。尽管在其他组件内使用组件是可以的,但你不应该在其他组件内部定义组件。这样做会导致每次包含组件重新渲染时组件都被重新定义,从而阻止React将其识别为相同类型的组件。这反过来会导致React重新挂载元素(即完全从DOM中删除它,然后在其位置添加一个新元素),而不仅仅是重新渲染它。

相反,你应该在顶层定义所有组件(即不嵌套在其他组件或函数内)。从包含组件中的任何变量,都可以作为props传递。

在你的情况下,应该像这样定义:

import * as React from "react";
import { styled } from "@mui/material/styles";
import Box from "@mui/material/Box";
import {
  TextField,
  Input,
  inputBaseClasses,
  FormControl,
  InputLabel
} from "@mui/material";
import { BiShow, BiHide } from "react-icons/bi";
import { useState } from "react";

// ... 组件的定义(略)

export default function Text({
  errors,
  label,
  rows,
  type,
  defaultValue,
  required,
  onChange,
  setValue,
  name
}) {
  const inputType = (type) => {
    switch (type) {
      case "multiline":
        return <Multiline label={label} onChange={onChange} rows={rows} />;
      case "password":
        return (
          <Password
            label={label}
            required={required}
            errors={errors}
            onChange={onChange}
            defaultValue={defaultValue}
          />
        );
      default:
        return (
          <TextInput
            label={label}
            required={required}
            type={type}
            onChange={onChange}
            defaultValue={defaultValue}
          />
        );
    }
  };

  return <Box>{inputType(type)}</Box>;
}

请注意,这只是你代码中的一部分,你需要在其他文件中将这些组件定义在顶层,不要嵌套在其他组件内部。

英文:

You are defining the PasswordIcon, Multiline, Password, and TextInput components inside of the Text component. Though it is fine to use components inside other components, you should never define components inside other components. Doing so causes the component to be redefined on each render of the containing component which then prevents React from recognizing it as the same type of component. This in turn causes React to remount the element (i.e. completely remove it from the DOM and then add a new element back into the DOM in its place) rather than just re-render it.

Instead, you should define all components at the top level (i.e. not nested within another component or function). Any variables from the containing component that the nested components were dependent on can be passed as props.

Here's what this would look like in your case:

import * as React from &quot;react&quot;;
import { styled } from &quot;@mui/material/styles&quot;;
import Box from &quot;@mui/material/Box&quot;;
import {
TextField,
Input,
inputBaseClasses,
FormControl,
InputLabel
} from &quot;@mui/material&quot;;
import { BiShow, BiHide } from &quot;react-icons/bi&quot;;
import { useState } from &quot;react&quot;;
const ValidationTextField = styled(TextField)({
&quot;&amp; input:valid + fieldset&quot;: {
borderColor: &quot;green&quot;,
borderWidth: 2
},
&quot;&amp; input:invalid + fieldset&quot;: {
borderColor: &quot;red&quot;,
borderWidth: 2
},
&quot;&amp;:hover:valid + fieldset&quot;: {
borderColor: &quot;yellow&quot;
},
&quot;&amp; input:valid:focus + fieldset&quot;: {
borderLeftWidth: 6,
borderColor: &quot;purple&quot;,
color: &quot;pink&quot;,
padding: &quot;4px !important&quot;
},
&quot;&amp; .MuiOutlinedInput-root&quot;: {
&quot;&amp;:hover fieldset&quot;: {
borderColor: &quot;yellow&quot;,
color: &quot;orange&quot;
}
}
});
const StyledInput = styled(Input)({
borderRadius: 4,
border: &quot;2px solid blue&quot;,
padding: 4,
[`&amp;.${inputBaseClasses.multiline}`]: {
height: &quot;auto&quot;,
border: &quot;2px solid red&quot;
}
});
const PassWordIcon = ({ showPass, setShowPass }) =&gt;
showPass ? (
&lt;BiHide size={40} onClick={() =&gt; setShowPass(false)} /&gt;
) : (
&lt;BiShow size={40} onClick={() =&gt; setShowPass(true)} /&gt;
);
const Multiline = ({ label, onChange, rows }) =&gt; {
return (
&lt;FormControl variant=&quot;outlined&quot;&gt;
&lt;InputLabel&gt;{label}&lt;/InputLabel&gt;
&lt;StyledInput
sx={{
&quot;&amp;:hover&quot;: {
border: &quot;2px solid yellow&quot;
},
&quot;&amp;.Mui-focused&quot;: {
borderColor: &quot;purple&quot;,
color: &quot;pink&quot;,
padding: &quot;4px !important&quot;
}
}}
onChange={(e) =&gt; onChange(e.target.value)}
disableUnderline
multiline
rows={rows}
/&gt;
&lt;/FormControl&gt;
);
};
const Password = ({ label, required, errors, onChange, defaultValue }) =&gt; {
const [showPass, setShowPass] = useState(false);
return (
&lt;Box&gt;
&lt;ValidationTextField
label={label}
required={required}
error={errors}
onChange={(e) =&gt; onChange(e.target.value)}
type={showPass ? &quot;text&quot; : &quot;password&quot;}
sx={{
input: {
color: &quot;red&quot;,
&quot;&amp;::placeholder&quot;: {
color: &quot;darkgreen&quot;,
&quot;&amp;:hover fieldset&quot;: {
color: &quot;orange&quot;
}
}
},
label: {
color: &quot;pink&quot;
}
}}
variant=&quot;outlined&quot;
defaultValue={defaultValue}
/&gt;
&lt;PassWordIcon showPass={showPass} setShowPass={setShowPass} /&gt;
&lt;/Box&gt;
);
};
const TextInput = ({ label, required, type, onChange, defaultValue }) =&gt; {
return (
&lt;ValidationTextField
label={label}
required={required}
type={type}
onChange={(e) =&gt; onChange(e.target.value)}
sx={{
input: {
color: &quot;red&quot;,
&quot;&amp;::placeholder&quot;: {
color: &quot;darkgreen&quot;,
&quot;&amp;:hover fieldset&quot;: {
color: &quot;orange&quot;
}
}
},
label: {
color: &quot;pink&quot;
}
}}
variant=&quot;outlined&quot;
defaultValue={defaultValue}
/&gt;
);
};
export default function Text({
errors,
label,
rows,
type,
defaultValue,
required,
onChange,
setValue,
name
}) {
const inputType = (type) =&gt; {
switch (type) {
case &quot;multiline&quot;:
return &lt;Multiline label={label} onChange={onChange} rows={rows} /&gt;;
case &quot;password&quot;:
return (
&lt;Password
label={label}
required={required}
errors={errors}
onChange={onChange}
defaultValue={defaultValue}
/&gt;
);
default:
return (
&lt;TextInput
label={label}
required={required}
type={type}
onChange={onChange}
defaultValue={defaultValue}
/&gt;
);
}
};
return &lt;Box&gt;{inputType(type)}&lt;/Box&gt;;
}

Textfield 在 React 的 onChange 后失去焦点。

Related answers (same root cause):

Related documentation: https://react.dev/learn/your-first-component#nesting-and-organizing-components

Excerpt:

> Components can render other components, but you must never nest their definitions.
> Instead, define every component at the top level.

huangapple
  • 本文由 发表于 2023年4月17日 23:04:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76036577.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定