繁体   English   中英

接受 TextArea 和 Input 的 onChange 的 Typescript 和 React 组件

[英]Typescript & React Component that accepts onChange for both TextArea and Input

我是 typescript 的新手,我正在尝试创建一个输入组件,如果它接收到type="text"它会呈现一个input ,如果它接收到type="textarea"它会呈现一个textarea 问题是当我在代码中将组件与onChange一起使用时,打字稿会抱怨,似乎它不允许我在同一事件上使用两种类型?

它告诉我:

Type '(e: ChangeEvent<HTMLInputElement>) => void' is not assignable to type '(e?: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement> | undefined) => void'.
Types of parameters 'e' and 'e' are incompatible.
Type 'ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement> | undefined' is not assignable to type 'ChangeEvent<HTMLInputElement>'.
Type 'undefined' is not assignable to type 'ChangeEvent<HTMLInputElement>'.

输入.js

interface InputProps {
  className?: string;
  type?: string;
  placeholder?: string;
  onChange?: (e?: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
}

const Input: FunctionComponent<InputProps> = ({ type = 'text', ...props }) => {
  if (type === 'textarea') {
    return <textarea {...props} />;
  }
  return <input type={type} {...props} />;
};

用法:

class Usage extends React.Component<State> {
  state: State;

  onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ input: e.target.value });
  };

  render() {
    return (
        <Input placeholder="Write an something..." onChange={this.onInputChange} />
    );
  }
}

我该如何解决?


更新

我目前解决它的方法是说 event 可以是any类型,但这是一种 hack

type CommonProps = {
  className?: string;
  placeholder?: string;
  type?: string;
  onChange?: (e: any) => void;
};

您可以使用可区分的联合来传递两种类型的参数,一种用于text ,另一种用于textarea 这具有确保处理程序和类型同步的额外好处。

type InputProps = { // The common Part
    className?: string;
    placeholder?: string;
} & ({ // The discriminated union
    type?: "text";
    onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
} | {
    type: "textarea";
    onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
})

const Input: FunctionComponent<InputProps> = (props: InputProps) => {
    if (props.type === 'textarea') {
        return <textarea {...props} />;
    }
    return <input {...props} />;
};


class Usage extends React.Component<State> {
    state: State;

    onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.setState({ input: e.target.value });
    };

    render() {
        return (
            <Input placeholder="Write an something..." onChange={this.onInputChange} />
        );
    }
}

如果您使用的是打字稿,则需要创建一个类。 正常功能不允许| 在打字稿道具类型中。

这应该是您的Input.js文件:

export interface IInputProps {
  className?: string;
  type?: string;
  placeholder?: string;
  onChange?: (e?: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
}

export class Input extends React.Component<IInputProps, {}> {
  constructor(props: IInputProps) {
    super(props);
  }
  render() {
    const { props, props: { type } } = this;
    if (type === 'textarea') {
      return <textarea {...props} />;
    }
    return <input type={type} {...props} />;
  }
}

以下是它的使用方法:

class Usage extends React.Component<State> {
  state: State;

  onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ input: e.target.value });
  };

  render() {
    return (
      <Input placeholder="Write an something..." onChange={this.onInputChange} />
    );
  }
}

这是它将如何评估它是Input还是TextArea

在此处输入图像描述

React.js TypeScript 相同的组件<input>和<textarea></textarea> &lt;/div&gt;<div id="text_translate"><p> 我创建了一个带有一些基本辅助组件的&lt;Input /&gt;组件,以提高可用性并最小化主组件内的代码。 但是,我还想创建一个&lt;textarea /&gt; HTML 元素。 我将<strong>React.js</strong>与<strong>TypeScript</strong>一起使用。 因此,道具,尤其是输入事件道具相互覆盖,编译器会抱怨。 除了在&lt;Multiline /&gt;组件中编写相同的代码之外,我能做些什么吗?</p><p> <em><strong>注意</strong>:我用React.forwardRef包装组件,用于转发并将ref传递给输入组件</em></p><h3>更新:添加代码片段</h3><p><strong>输入/输入.tsx</strong></p><pre> export default class Input extends React.Component&lt;FProps, State&gt; { static displayName = "Input"; state: State = { id: this.props.id || "", value: this.props.value || this.props.defaultValue || "", }; componentDidMount() { if (.this.state.id) { this.setState(prevState =&gt; ({..,prevState: id; generateInputId() })): } } handleChange = (e. React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; { const newVal = e.target;value. const { onChange } = this;props. this:setState({ value; newVal }); if (onChange) { onChange(e); } }, render() { const { id. value } = this;state. return ( &lt;InputContextProvider value={{...this,props, value, id: onChange. this;handleChange }}&gt; &lt;InputContainer&gt; &lt;FieldContainer /&gt; &lt;/InputContainer&gt; &lt;/InputContextProvider&gt; ); } }</pre><p> <strong>输入/<strong>助手</strong>/FieldContainer.tsx</strong></p><pre> export const FieldContainer: React.FunctionComponent = () =&gt; { const { value: propsValue, type, label, id, defaultValue, className, state = "default", placeholder, floatingplaceholder, prefix, suffix, characterLimit, maxLength, allowClear, onChange, forwardref, ...rest } = useInputContext(); const isDisabled = useDisabled(rest, state); const [value, setValue] = useState&lt;string | number&gt;(propsValue || defaultValue || ""); const [prefixWidth, setPrefixWidth] = useState&lt;number&gt;(0); const [suffixWidth, setSuffixWidth] = useState&lt;number&gt;(0); const prefixRef = useRef&lt;HTMLDivElement&gt;(null); const suffixRef = useRef&lt;HTMLDivElement&gt;(null); // TODO: Replace with &lt;Icon /&gt; component if (allowClear) { // suffix = &lt;&gt; } const handleChange = (e: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; { const newVal = e.target.value; if (characterLimit &amp;&amp; maxLength &amp;&amp; String(newVal).length &gt; maxLength) { return; } setValue(newVal); if (onChange) { onChange(e); } }; useEffect(() =&gt; { if (prefixRef &amp;&amp; prefixRef.current) { const prefixWidth = prefixRef.current.offsetWidth; setPrefixWidth(prefixWidth); } if (suffixRef &amp;&amp; suffixRef.current) { const suffixWidth = suffixRef.current.offsetWidth; setSuffixWidth(suffixWidth); } }, [prefix, suffix]); return ( &lt;OuterFieldContainer prefixWidth={prefixWidth}&gt; &lt;FixGroup group={prefix} ref={prefixRef} position="left" /&gt; &lt;Base id={id} value={value} className={className} state={state} placeholder={floatingplaceholder === false &amp;&amp; placeholder? placeholder: undefined} disabled={isDisabled} onChange={e =&gt; handleChange(e)} ref={forwardref} style={{ paddingLeft: prefix? `${(prefixWidth + DEFAULT_PADDING) / EM_REM_MULTIPLIER}em`: "", paddingRight: suffix? `${(suffixWidth + DEFAULT_PADDING) / EM_REM_MULTIPLIER}em`: "", }} maxLength={maxLength} {...rest} /&gt; &lt;FixGroup group={suffix} ref={suffixRef} position="right" /&gt; &lt;/OuterFieldContainer&gt; ); };</pre><p> <strong>输入/<strong>助手</strong>/Base/Base.tsx</strong></p><pre> export const Base = React.forwardRef&lt;HTMLInputElement&gt;((componentProps, ref) =&gt; { const { id: propsId, value, placeholder, type, floatingplaceholder, state = "default", onChange, style, children: propsChildren, characterLimit, overrideOnChange, allowClear, ...props } = useInputContext(); const id = useInputId(propsId); const [classNames, rest] = useClassnames("input", props, { stateToRemove: { state } }); const isDisabled = useDisabled(props, state); // return type === "textarea"? ( // &lt;textarea // id={id} // className={classNames} // value={value || ""} // placeholder={placeholder} // disabled={isDisabled} // aria-disabled={isDisabled} // aria-label={placeholder} // ref={ref} // // * Enable / disabled the Grammarly extension // // data-gramm="false" // cols={28} // rows={5} // {...rest} // /&gt; // ): ( return ( &lt;input id={id} type={type} className={classNames} value={value} placeholder={?floatingplaceholder: placeholder. undefined} disabled={isDisabled} aria-disabled={isDisabled} aria-label={placeholder} ref={ref} data-hasfloatingplaceholder={floatingplaceholder} data-testid="input" onChange={e =&gt; onChange &amp;&amp; onChange(e)} style={style} {..;rest} /&gt; ); });</pre></div>

[英]React.js TypeScript Same component for <input /> and <textarea />

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将带有 typescript 的 React Native 中的 UseState 传递给输入框组件并在 onChange 上更新它 React.js TypeScript 相同的组件<input>和<textarea></textarea> &lt;/div&gt;<div id="text_translate"><p> 我创建了一个带有一些基本辅助组件的&lt;Input /&gt;组件,以提高可用性并最小化主组件内的代码。 但是,我还想创建一个&lt;textarea /&gt; HTML 元素。 我将<strong>React.js</strong>与<strong>TypeScript</strong>一起使用。 因此,道具,尤其是输入事件道具相互覆盖,编译器会抱怨。 除了在&lt;Multiline /&gt;组件中编写相同的代码之外,我能做些什么吗?</p><p> <em><strong>注意</strong>:我用React.forwardRef包装组件,用于转发并将ref传递给输入组件</em></p><h3>更新:添加代码片段</h3><p><strong>输入/输入.tsx</strong></p><pre> export default class Input extends React.Component&lt;FProps, State&gt; { static displayName = "Input"; state: State = { id: this.props.id || "", value: this.props.value || this.props.defaultValue || "", }; componentDidMount() { if (.this.state.id) { this.setState(prevState =&gt; ({..,prevState: id; generateInputId() })): } } handleChange = (e. React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; { const newVal = e.target;value. const { onChange } = this;props. this:setState({ value; newVal }); if (onChange) { onChange(e); } }, render() { const { id. value } = this;state. return ( &lt;InputContextProvider value={{...this,props, value, id: onChange. this;handleChange }}&gt; &lt;InputContainer&gt; &lt;FieldContainer /&gt; &lt;/InputContainer&gt; &lt;/InputContextProvider&gt; ); } }</pre><p> <strong>输入/<strong>助手</strong>/FieldContainer.tsx</strong></p><pre> export const FieldContainer: React.FunctionComponent = () =&gt; { const { value: propsValue, type, label, id, defaultValue, className, state = "default", placeholder, floatingplaceholder, prefix, suffix, characterLimit, maxLength, allowClear, onChange, forwardref, ...rest } = useInputContext(); const isDisabled = useDisabled(rest, state); const [value, setValue] = useState&lt;string | number&gt;(propsValue || defaultValue || ""); const [prefixWidth, setPrefixWidth] = useState&lt;number&gt;(0); const [suffixWidth, setSuffixWidth] = useState&lt;number&gt;(0); const prefixRef = useRef&lt;HTMLDivElement&gt;(null); const suffixRef = useRef&lt;HTMLDivElement&gt;(null); // TODO: Replace with &lt;Icon /&gt; component if (allowClear) { // suffix = &lt;&gt; } const handleChange = (e: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; { const newVal = e.target.value; if (characterLimit &amp;&amp; maxLength &amp;&amp; String(newVal).length &gt; maxLength) { return; } setValue(newVal); if (onChange) { onChange(e); } }; useEffect(() =&gt; { if (prefixRef &amp;&amp; prefixRef.current) { const prefixWidth = prefixRef.current.offsetWidth; setPrefixWidth(prefixWidth); } if (suffixRef &amp;&amp; suffixRef.current) { const suffixWidth = suffixRef.current.offsetWidth; setSuffixWidth(suffixWidth); } }, [prefix, suffix]); return ( &lt;OuterFieldContainer prefixWidth={prefixWidth}&gt; &lt;FixGroup group={prefix} ref={prefixRef} position="left" /&gt; &lt;Base id={id} value={value} className={className} state={state} placeholder={floatingplaceholder === false &amp;&amp; placeholder? placeholder: undefined} disabled={isDisabled} onChange={e =&gt; handleChange(e)} ref={forwardref} style={{ paddingLeft: prefix? `${(prefixWidth + DEFAULT_PADDING) / EM_REM_MULTIPLIER}em`: "", paddingRight: suffix? `${(suffixWidth + DEFAULT_PADDING) / EM_REM_MULTIPLIER}em`: "", }} maxLength={maxLength} {...rest} /&gt; &lt;FixGroup group={suffix} ref={suffixRef} position="right" /&gt; &lt;/OuterFieldContainer&gt; ); };</pre><p> <strong>输入/<strong>助手</strong>/Base/Base.tsx</strong></p><pre> export const Base = React.forwardRef&lt;HTMLInputElement&gt;((componentProps, ref) =&gt; { const { id: propsId, value, placeholder, type, floatingplaceholder, state = "default", onChange, style, children: propsChildren, characterLimit, overrideOnChange, allowClear, ...props } = useInputContext(); const id = useInputId(propsId); const [classNames, rest] = useClassnames("input", props, { stateToRemove: { state } }); const isDisabled = useDisabled(props, state); // return type === "textarea"? ( // &lt;textarea // id={id} // className={classNames} // value={value || ""} // placeholder={placeholder} // disabled={isDisabled} // aria-disabled={isDisabled} // aria-label={placeholder} // ref={ref} // // * Enable / disabled the Grammarly extension // // data-gramm="false" // cols={28} // rows={5} // {...rest} // /&gt; // ): ( return ( &lt;input id={id} type={type} className={classNames} value={value} placeholder={?floatingplaceholder: placeholder. undefined} disabled={isDisabled} aria-disabled={isDisabled} aria-label={placeholder} ref={ref} data-hasfloatingplaceholder={floatingplaceholder} data-testid="input" onChange={e =&gt; onChange &amp;&amp; onChange(e)} style={style} {..;rest} /&gt; ); });</pre></div> React.JS Typescript - OnChange 表示状态对象的“组件正在将类型文本的受控输入更改为在 OnChange 中不受控制” 每次更新 React 中的输入或文本区域时,onChange 事件处理程序是否会重新呈现组件? React Typescript 故事书通过 onChange callBack 实现自定义 Input 组件,然后 setState value backTo Input Typescript React 输入 onchange 不安全赋值 'any' 反应 Typescript 输入 onChange 事件类型? React onChange 不适用于功能组件内的输入 反应输入组件被存储覆盖 onChange 对输入做出反应 onChange 重新呈现整个组件
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM