首页 > 解决方案 > 为什么每次击键后 React 文本输入都会失去焦点?

问题描述

在使用 Material-UI v4.11.0 的 React v16.13.1 中,我有以下组件

  const GuestSignup = (props: GuestSignupProps) => {

    return <Grid container spacing={1} alignItems="flex-end">
      <Grid item xs={1}>
        <Tooltip title="Uncheck to cancel individual signup" arrow>
          <FormControl>
            <FormControlLabel
              control={<Checkbox key="ck1" onChange={props.handleSignupCheckChangeGuest(props.gs.guestSignupID === global.NULL_ID ? props.gs.index : props.gs.guestSignupID)} value={props.gs.guestSignupID} checked={props.gs.selected} />}
              label=""
            />
          </FormControl>
        </Tooltip>
      </Grid>
      <Grid item xs={2}>
        <TextField label="First Name" value={props.gs.firstName} key={props.gs.guestSignupID.toString() + 'fn'} required onChange={props.handleFirstNameChange(props.gs.guestSignupID, props.gs.index)} />
      </Grid>
      <Grid item xs={2}>
        <TextField label="Last Name" value={props.gs.lastName} key={props.gs.guestSignupID.toString() + 'ln'} required onChange={props.handleLastNameChange(props.gs.guestSignupID, props.gs.index)} />
      </Grid>
      <Grid item xs={2}>
        <GuestTypes value={props.gs.guestType.guestTypeID} key="gt" gt={props.gt} handleGuestTypeChange={props.handleGuestTypeChange(props.gs.guestSignupID, props.gs.index)} />
      </Grid>
      <Grid item xs={2}>
        <KeyboardDatePicker
          autoOk
          className={classes.guestDate}
          disablePast
          disableToolbar
          variant="inline"
          format="MM/dd/yyyy"
          key="ad"
          margin="dense"
          label="Arrival"
          required
          value={props.gs.arrivalDate === global.EMPTY_STRING ? null : new Date(props.gs.arrivalDate)}
          onChange={props.handleArrivalDateChange(props.gs.guestSignupID === global.NULL_ID ? props.gs.index : props.gs.guestSignupID, GUEST_SIGNUP)}
          KeyboardButtonProps={{
            'aria-label': 'change date',
          }}
        />
      </Grid>
      <Grid item xs={2}>
        <KeyboardDatePicker
          autoOk
          className={classes.guestDate}
          disablePast
          disableToolbar
          variant="inline"
          format="MM/dd/yyyy"
          key="dd"
          margin="dense"
          label="Departure"
          required
          value={props.gs.departureDate === global.EMPTY_STRING ? null : new Date(props.gs.departureDate)}
          onChange={props.handleDepartureDateChange(props.gs.guestSignupID === global.NULL_ID ? props.gs.index : props.gs.guestSignupID, GUEST_SIGNUP)}
          KeyboardButtonProps={{
            'aria-label': 'change date',
          }}
        />
      </Grid>
    </Grid>
  }

使用这些输入道具

interface GuestSignupProps {
  id: number;
  key: number;
  gs: GuestSignup;
  gt: Array<GuestType>;
  handleArrivalDateChange(id: number, type: string): (date: Date | null) => void;
  handleDepartureDateChange(id: number, type: string): (date: Date | null) => void;
  handleFirstNameChange(id: number, index: number): (event: React.ChangeEvent<HTMLInputElement>) => void;
  handleGuestTypeChange(id: number, index: number): (event: React.ChangeEvent<{ value: unknown }>) => void;
  handleLastNameChange(id: number, index: number): (event: React.ChangeEvent<HTMLInputElement>) => void;
  handleSignupCheckChangeGuest(id: number): (event: React.ChangeEvent<HTMLInputElement>) => void;
}

它使用这种数据结构

export interface GuestSignup {
  guestSignupID: number;
  index: number;
  signupID: number;
  firstName: string;
  lastName: string;
  gender: string;
  guestType: GuestType;
  arrivalDate: string;
  departureDate: string;
  cancelDate: string;
  notes: string;
  cancel: boolean;
  selected: boolean;
}

它作为多个实例的数组存储在状态中

  const [guestSignups, setGuestSignups] = React.useState<Array<GuestSignup>>([]);

并在页面中有多个 GuestSignup 实例的情况下呈现。

  {guestSignups.length > 0 &&
    <React.Fragment>
      {
        guestSignups.map((gs: GuestSignup, index: number) => (
          <GuestSignup id={gs.guestSignupID} key={gs.guestSignupID > 0 ? gs.guestSignupID : index} gs={gs} gt={guestTypes} handleArrivalDateChange={handleArrivalDateChange} handleDepartureDateChange={handleDepartureDateChange} handleFirstNameChange={handleFirstNameChange} handleGuestTypeChange={handleGuestTypeChange} handleLastNameChange={handleLastNameChange} handleSignupCheckChangeGuest={handleSignupCheckChangeGuest} />
        ))
      }
    </React.Fragment>
  }

名字和姓氏都有处理程序,每次击键都会在 onChange 中调用

  const handleFirstNameChange = (id: number, index: number) => (event: React.ChangeEvent<HTMLInputElement>) => {

    // Used either GuestSignupID for existing guests or array index for new guests
    const newGuestSignup = produce(guestSignups,
      draft => {
        const i = draft.findIndex(gs => { return gs.guestSignupID > 0 ? gs.guestSignupID === id : gs.index === index });
        draft[i].firstName = event.target.value;
      }
    );

    setGuestSignups([ ...newGuestSignup ]);

  }

问题是每次击键后,名字和姓氏文本输入都会失去焦点。他们在兄弟姐妹中拥有唯一的钥匙。似乎整个访客注册集在处理程序之后重新呈现,因为如果您点击 TAB,最顶层访客注册中的第一个复选框将获得焦点。

使文本输入元素在每次击键后保持焦点的正确方法是什么?

标签: javascriptreactjstypescriptmaterial-ui

解决方案


几个月前我也遇到了同样的问题。您需要从所有TextField标记中删除key属性。如果你添加key属性,如果 key 发生变化,React 将重新渲染组件,并且你的输入将失去焦点。你可以从这里阅读更多关于 key 的信息。


推荐阅读