javascript - React 初学者问题 : Textfield Losing Focus On Update

标签 javascript reactjs forms focus material-ui

我编写了一个组件,该组件应该列出一堆带有相应文本字段的复选框。当您单击复选框或在字段中键入内容时,它意味着更新状态。

文本框工作正常,但当我在字段中输入时,它更新状态正常,但每当我点击键盘时我就会失去焦点。

我意识到这可能是因为没有设置键,所以我为所有内容都添加了键,但它仍然失去焦点。有一次我尝试在我的事件中添加 stopPropegation 因为我认为这可能会导致问题?我不确定..仍在学习中...似乎没有用,所以我也删除了那部分。

似乎仍然无法弄清楚是什么导致它失去焦点...有人对此问题有任何建议/解决方案吗?

我合并了我的代码并删除了不必要的部分以使其更易于阅读。有三个相关的 JS 文件..请看下面:

我仍然是初学者/学习者,所以如果您有与此代码的任何部分相关的有用建议,请随时提供。谢谢!

App.js

import React, { Component } from 'react';
import Form from './Form'

class App extends Component {
constructor() {
  super();
  this.state = {
      mediaDeliverables: [
        {label: 'badf', checked: false, quantity:''},
        {label: 'adfadf', checked: false, quantity:''},
        {label: 'adadf', checked: false, quantity:''},
        {label: 'addadf', checked: false, quantity:''},
        {label: 'adfdes', checked: false, quantity:''},
        {label: 'hghdgs', checked: false, quantity:''},
        {label: 'srtnf', checked: false, quantity:''},
        {label: 'xfthd', checked: false, quantity:''},
        {label: 'sbnhrr', checked: false, quantity:''},
        {label: 'sfghhh', checked: false, quantity:''},
        {label: 'sssddrr', checked: false, quantity:''}
      ]
  }
}

setMediaDeliverable = (value, index) => {
  let currentState = this.getStateCopy();
  currentState.mediaDeliverables[index] = value;
  this.setState(currentState);
} 

getStateCopy = () => Object.assign({}, this.state);

render() {
  return (
    <div className="App">
          <Form 
            key="mainForm"
            mediaDeliverablesOptions={this.state.mediaDeliverables}
            setMediaDeliverable={this.setMediaDeliverable}
          />  
    </div>
  );
  }
}
export default App;

表单.js

import React from 'react';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import MediaDeliverablesCheckBox from './MediaDeliverablesCheckBox';


const useStyles = makeStyles(theme => ({
  container: {
    display: 'inline-block',
    flexWrap: 'wrap',
  },
  root: {
    display: 'inline-block',
    flexWrap: 'wrap',
    maxWidth: 600,
    textAlign: 'left',
  },
  extendedIcon: {
    marginRight: theme.spacing(1),
  },
  formControl: {
    margin: theme.spacing(1),
    minWidth: 120,
    maxWidth: 300,
  },
  textField: {
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1),
    width: 370,
  },
  dense: {
    marginTop: 19,
  },
  chips: {
    display: 'flex',
    flexWrap: 'wrap',
  },
  chip: {
    margin: 2,
  },
  noLabel: {
    marginTop: theme.spacing(3),
  },
}));

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
      width: 250,
    },
  },
};

function getStyles(name, accountName, theme) {
  // console.log('>> [form.js] (getStyles) ',accountName)
  return {
    fontWeight:
      accountName.indexOf(name) === -1
        ? theme.typography.fontWeightRegular
        : theme.typography.fontWeightMedium,
  };
}



export default function Form(props) {

  const mediaDeliverablesOptions = props.mediaDeliverablesOptions;
  const classes = useStyles();
  const theme = useTheme();

  const CheckboxGroup = ({ values, label, onChange }) => (
  <FormControl component="fieldset">
    <FormLabel component="legend">{label}</FormLabel>
    <FormGroup>
      {values.map((value, index) => (
        <FormControlLabel
          key={index}
          control={
            <Checkbox
              checked={value.checked}
              onChange={onChange(index)}
            />
          }
          label={value.label}
        />
      ))}
    </FormGroup>
  </FormControl>
);

  const MediaDeliverableCheckBoxList = ({values, label}) => (
    <FormControl component="fieldset">
    <FormLabel component="legend">{label}</FormLabel>
    <FormGroup>
    {values.map((value, index) => (
        <MediaDeliverablesCheckBox
        key={index}
        mediaDeliverablesOptions={value}
        onMediaDeliverableChange={onMediaDeliverableChange(index)}
      />
      ))}
    </FormGroup>
  </FormControl>
    );



  const onCheckBoxChange = index => ({ target: { checked } }) => {
    const newValues = [...values];
    const value = values[index];
    newValues[index] = { ...value, checked };
    props.setDesignOrDigital(newValues);
  };

  const onMediaDeliverableChange = index => (deliverableData, e) => {
    props.setMediaDeliverable(deliverableData, index);
  }

  return (
    <div className={classes.root}>
      <MediaDeliverableCheckBoxList
        label="Please Choose Deliverables:"
        values={mediaDeliverablesOptions}
        key="media-deliverable-checkbox-list"
      />
    </div>
  );
}

MediaDeliverablesCheckbox.js

import React from 'react';
import Checkbox from '@material-ui/core/Checkbox';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import TextField from '@material-ui/core/TextField';

export default function MediaDeliverablesCheckBox(props) {

  let deliverableData = Object.assign({}, props.mediaDeliverablesOptions);

  const onCheckBoxChange = (e) => {
     deliverableData.checked = e.target.checked;
     props.onMediaDeliverableChange(deliverableData, e);
  }

  const onQuantityChange = (e) => {
     deliverableData.quantity = e.target.value;
     props.onMediaDeliverableChange(deliverableData, e);
  }

  const CheckboxGroup = ({ value, label }) => (

  <FormControl component="fieldset">
    <FormGroup>
        <FormControlLabel
          control={
            <Checkbox
              key={props.index}
              checked={value.checked}
              onChange={onCheckBoxChange}
            />
          }
          label={label}
        />
    </FormGroup>
  </FormControl>
  );

return(
    <div className="MediaDeliverablesCheckBox">
      <CheckboxGroup
            key={props.index}
            label={props.mediaDeliverablesOptions.label}
            value={props.mediaDeliverablesOptions}
          />
      <TextField
        key={'tf'+props.index}
        id={'quantity-'+props.index}
        label="Quantity"
        placeholder="How many do you need?"
        multiline
        variant="outlined"
        value={props.mediaDeliverablesOptions.quantity}
        onChange={onQuantityChange}
        fullWidth
      />
    </div>
  );
}

根据 Ryan C 推荐的编辑更新了 Form.js。

import React from 'react';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import MediaDeliverablesCheckBox from './MediaDeliverablesCheckBox';


const useStyles = makeStyles(theme => ({
  container: {
    display: 'inline-block',
    flexWrap: 'wrap',
  },
  root: {
    display: 'inline-block',
    flexWrap: 'wrap',
    maxWidth: 600,
    textAlign: 'left',
  },
  extendedIcon: {
    marginRight: theme.spacing(1),
  },
  formControl: {
    margin: theme.spacing(1),
    minWidth: 120,
    maxWidth: 300,
  },
  textField: {
    marginLeft: theme.spacing(1),
    marginRight: theme.spacing(1),
    width: 370,
  },
  dense: {
    marginTop: 19,
  },
  chips: {
    display: 'flex',
    flexWrap: 'wrap',
  },
  chip: {
    margin: 2,
  },
  noLabel: {
    marginTop: theme.spacing(3),
  },
}));

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
      width: 250,
    },
  },
};

function getStyles(name, accountName, theme) {
  return {
    fontWeight:
      accountName.indexOf(name) === -1
        ? theme.typography.fontWeightRegular
        : theme.typography.fontWeightMedium,
  };
}


// Failed to compile
// ./src/Form.js
//   Line 86:  Parsing error: Unexpected token, expected ","

//   84 | 
//   85 | const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
// > 86 |     {values.map((value, index) => (
//      |            ^
//   87 |         <MediaDeliverablesCheckBox
//   88 |         key={index}
//   89 |         index={index}
// This error occurred during the build time and cannot be dismissed.

const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
    {values.map((value, index) => (
        <MediaDeliverablesCheckBox
        key={index}
        index={index}
        mediaDeliverablesOptions={value}
        onMediaDeliverableChange={onMediaDeliverableChange(index)}
      />
      ))}
);

export default function Form(props) {

  const mediaDeliverablesOptions = props.mediaDeliverablesOptions;
  const classes = useStyles();
  const theme = useTheme();

  const CheckboxGroup = ({ values, label, onChange }) => (
  <FormControl component="fieldset">
    <FormLabel component="legend">{label}</FormLabel>
    <FormGroup>
      {values.map((value, index) => (
        <FormControlLabel
          key={index}
          control={
            <Checkbox
              checked={value.checked}
              onChange={onChange(index)}
            />
          }
          label={value.label}
        />
      ))}
    </FormGroup>
  </FormControl>
);


  const onCheckBoxChange = index => ({ target: { checked } }) => {
    const newValues = [...values];
    const value = values[index];
    newValues[index] = { ...value, checked };
    props.setDesignOrDigital(newValues);
  };

  const onMediaDeliverableChange = index => (deliverableData, e) => {
    props.setMediaDeliverable(deliverableData, index);
  }

  return (
    <div className={classes.root}>
      <MediaDeliverableCheckBoxList
        onMediaDeliverableChange={onMediaDeliverableChange}
      />
    </div>
  );
}

最佳答案

我看到两个主要问题:

  1. 您如何定义不同的组件(嵌套组件类型)
  2. 不将 index prop 传递给需要它的组件

你有以下结构(省略与我的观点没有直接关系的细节):

export default function Form(props) {

  const onMediaDeliverableChange = index => (deliverableData, e) => {
    props.setMediaDeliverable(deliverableData, index);
  }

  const MediaDeliverableCheckBoxList = ({values, label}) => (
    <FormGroup>
    {values.map((value, index) => (
        <MediaDeliverablesCheckBox key={index} onMediaDeliverableChange={onMediaDeliverableChange(index)}/>
      ))}
    </FormGroup>
    );

  return (
      <MediaDeliverableCheckBoxList/>
  );
}

函数MediaDeliverableCheckBoxList表示用于呈现 <MediaDeliverableCheckBoxList/> 的组件类型元素。每当Form由于 Prop 或状态改变而重新渲染,React 将重新渲染其子级。如果特定子项的组件类型相同(加上一些其他条件,例如 key 如果指定相同),那么它将更新现有的 DOM 节点。如果特定子项的组件类型不同,则相应的 DOM 节点将被删除,并将新节点添加到 DOM。

通过定义 MediaDeliverableCheckBoxList组件类型 Form功能,您导致该组件类型在每次渲染时都不同。这将导致所有 DOM 节点被替换而不仅仅是更新,这将导致焦点在先前具有焦点的 DOM 节点被移除时消失。它还会导致性能显着下降。

您可以通过将此组件类型移出 Form 来解决此问题函数,然后添加所需的任何其他 Prop (例如 onMediaDeliverableChange )以传达 Form 内部已知的上下文.您还需要将索引作为 Prop 传递给 MediaDeliverablesCheckBox因为它正在使用它。

const MediaDeliverableCheckBoxList = ({values, label, onMediaDeliverableChange}) => (
    <FormGroup>
    {values.map((value, index) => (
        <MediaDeliverablesCheckBox key={index} index={index} onMediaDeliverableChange={onMediaDeliverableChange(index)}/>
      ))}
    </FormGroup>
);


export default function Form(props) {

  const onMediaDeliverableChange = index => (deliverableData, e) => {
    props.setMediaDeliverable(deliverableData, index);
  }


  return (
      <MediaDeliverableCheckBoxList onMediaDeliverableChange={onMediaDeliverableChange}/>
  );
}

您对 CheckboxGroup 也有同样的问题可能还有其他组件。

关于javascript - React 初学者问题 : Textfield Losing Focus On Update,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56873912/

相关文章:

javascript - NodeJS/SailsJS 中 next() 的具体作用是什么?

javascript - 当子组件通过父组件过滤时,如何维护子组件的状态?

javascript - 如何在 JavaScript 字符串中包含变量

javascript - 交替背景颜色 react-bootstrap-table

jquery - 如何在 django 提交表单中使用 jquery post

php - 在php中提交表单后如何显示错误消息?

html - 在 Go 中解析 HTML 输入标签

javascript - 我正在尝试通过 setInterval() 重新渲染我的哈巴狗模板。这可能吗?

javascript - React Redux 身份验证

javascript - 用于测试 React 应用程序的全局导入