網站搜尋

javascript - 自動建議 renderInputComponent - inputProps 屬性 'onChange' 的類型不相容


當我在使用帶有自訂輸入 styled-component 的 react-autosuggest 時,我遇到了這個 Typescript 錯誤。

屬性 'onChange' 的類型不相容。類型 '(event: FormEvent, params: ChangeEvent) => void' 無法賦值給類型 '(event: ChangeEvent) => void'。

程式碼:(注意這不是完整的,只是相關部分)

// styled.ts
export const Input = styled.input`
  // styles
`;

// index.tsx
function renderInput(
  inputProps: Autosuggest.InputProps<SuggestionSearch>,
  placeholder: string
) {
  // --> error here
  return <Input {...inputProps} placeholder={placeholder} />;
}

const MyComponent = () => {
  const autosuggestRef = React.useRef<
    Autosuggest<SuggestionSearch, SuggestionSearch>
  >(null);

  const onChange = (event: React.FormEvent, { newValue }: ChangeEvent) =>
    setValue(newValue);

  const inputProps = {
    value,
    onChange,
    onKeyPress: onEnterPress,
  };

  return (
    <Autosuggest
      ref={autosuggestRef}
      renderInputComponent={props => renderInput(props, placeholder)}
      inputProps={inputProps}
      // other props
    />
  )
}

不確定如何解決這個問題,因為自動建議的 onChange 函數會覆蓋基本輸入的 onChange 屬性。

答案#1

我幾乎整晚都在自己處理這個問題。看起來這是一個錯誤。這是 InputProps 的簽名:

    interface InputProps<TSuggestion>
        extends Omit<React.InputHTMLAttributes<any>, 'onChange' | 'onBlur'> {
        onChange(event: React.FormEvent<any>, params: ChangeEvent): void;
        onBlur?(event: React.FocusEvent<any>, params?: BlurEvent<TSuggestion>): void;
        value: string;
    }

看起來我們需要自己建立一個 Input 元件,它能接受這種類型的 onChange 簽名,或者將這個 onChange 包裝在標準的 onChange 中。

所以,這是我的做法:

const renderInputComponent = (inputProps: InputProps<TSuggestion>): React.ReactNode => {
    const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement>): void => {
      inputProps.onChange(event, { newValue: event.target.value, method: 'type' });
    };
   return <Input {...inputProps} onChange={onChangeHandler} />;
}

兩個潛在的錯誤:

  • 請注意,返回的 event 是 React.ChangeEvent 而不是 React.FormEvent。這是小差異,但如果你實際使用它並且對事件特別在意,可能會在執行時造成問題。
  • 返回的 params: ChangeEvent 僅包含 type 事件方法。如果你需要其他的,比如 click、esc 等,你必須自行補充(例如透過 onKeyUp,然後再調用 `onChange(...{method: 'esc'})

根據文件:

注意:使用 renderInputComponent 時,您仍然需要指定通常的 inputProps。Autosuggest 會將您提供的 inputProps 與其他為了可及性所需的屬性(例如 'aria-activedescendant')合併,並將合併後的 inputProps 傳遞給 renderInputComponent。

你不必這樣做:

renderInputComponent={props => renderInput(props, placeholder)}

只需將 placeholder 直接傳入你的 inputProps,它就會在 renderInputComponent 中直接回傳給你。

  const inputProps = {
    value,
    onChange,
    onKeyPress: onEnterPress,
    placeholder: 'something!`
  };

答案#2

我的問題是 renderInputComponent 上的 onChange() 並不會觸發 onSuggestionsFetchRequested。

如果我不使用自訂輸入(透過 renderInputComponent),那麼一切都運作正常——onSuggestionsFetchRequested 會被觸發,建議列表會顯示。

主要成分:

    const MySearchBox: FC<MySearchBoxProps> = (props) => {
    
    // Autosuggest will pass through all these props to the input.
    const inputProps = {
        placeholder: props.placeholder,
        value: props.value,
        onChange: props.onChange
    };

    return (
        <Autosuggest
            suggestions={props.suggestions}
            onSuggestionsFetchRequested={props.onSuggestionsFetechRequested}
            onSuggestionsClearRequested={props.onSuggestionsClearRequested}
            getSuggestionValue={props.getSuggestionValue}
            shouldRenderSuggestions={(value) => value.trim().length > 2}
            renderSuggestion={(item: ISuggestion) => {
                <div className={classes.suggestionsList}>{`${item.text} (${item.id})`}</div>;
            }}
            renderInputComponent={(ip: InputProps<ISuggestion>) => {
                const params: InputProps<ISuggestion> = {
                    ...ip,
                    onChange: props.onChange
                };
                return renderInput(params);
            }}
            inputProps={inputProps}
            
        />
    );
};

export { MySearchBox };

自訂輸入元件:

import React, { FC, ChangeEvent } from 'react';
import SearchIcon from '@material-ui/icons/Search';
import useStyles from './searchInput.styles';
import { Input } from '@material-ui/core';

type SearchInputBoxProps = {
    loading?: boolean;
    searchIconName: string;
    minWidth?: number;
    placeHolder?: string;
    onChange?: (e: ChangeEvent, params: { newValue: string; method: string }) => void;
};

const SearchInputBox: FC<SearchInputBoxProps> = (props) => {
    const classes = useStyles();

    return (
        <div className={classes.root}>
            <div className={classes.searchIcon}>
                <SearchIcon />
            </div>

            <Input
                placeholder={props.placeHolder}
                classes={{
                    root: classes.inputContainer,
                    input: classes.inputBox
                }}
                inputProps={{ 'aria-label': 'search' }}
                onChange={(e) => {
                    const params: { newValue: string; method: string } = {
                        newValue: e.target.value,
                        method: 'type'
                    };
                    console.log(`e: ${JSON.stringify(params)}`);
                    if (props.onChange) props.onChange(e, params);
                }}
                //onMouseOut={(e) => alert(e.currentTarget.innerText)}
            />
        </div>
    );
};

export { SearchInputBox };

渲染函數:

const renderInput = (inputProps: InputProps<ISuggestion>): React.ReactNode => {
    return <SearchInputBox loading={false} onChange={inputProps.onChange} placeHolder={inputProps.placeholder} searchIconName={'search'} {...inputProps.others} />;
};