React 初学者问题:Textfield 失去对更新的关注

Cma*_*ter 9 javascript forms focus reactjs material-ui

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

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

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

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

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

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

应用程序.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;
Run Code Online (Sandbox Code Playgroud)

表单.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>
  );
}
Run Code Online (Sandbox Code Playgroud)

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

根据 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>
  );
}
Run Code Online (Sandbox Code Playgroud)

Rya*_*ell 5

我看到两个主要问题:

  1. 如何定义不同的组件(嵌套组件类型)
  2. 不将索引属性传递给期望它的组件

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

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/>
  );
}
Run Code Online (Sandbox Code Playgroud)

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

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

您可以通过将此组件类型移动到函数之外Form,然后添加所需的任何其他道具(例如onMediaDeliverableChange)来传达 内部已知的上下文来解决此问题Form。您还需要将索引作为道具传递给它,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}/>
  );
}
Run Code Online (Sandbox Code Playgroud)

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