我编写了一个组件,该组件应该列出一堆带有相应文本字段的复选框。当您单击复选框时,或在字段中输入内容是为了更新状态。
文本框工作正常,但是当我在字段中键入内容时,它会更新状态确定,但是每当我敲击键盘时,我都会失去焦点。
我意识到这可能是由于没有设置键,所以我向所有内容添加了键,但仍然失去了重点。有一次我尝试在事件中添加stopPropegation,因为我认为这可能会引起问题?我不确定...仍在学习...似乎没有用,所以我也删除了该部分。
Still can't seem to figure out what is causing it to lose focus... does anyone have any advice/solves for this issue?
I consolidated my code and cut out the unnecessary bits to make it easier to read. There are three relevant JS files.. please see below:
I'm still a beginner/learning so if you have useful advice related to any part of this code, feel free to offer. Thanks!
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;
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) {
// 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>
);
}
Updated Form.js based on recommended edits by Ryan C.
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>
);
}
I see two main issues:
You have the following structure (leaving out details that are not directly related to my point):
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/>
);
}
The function MediaDeliverableCheckBoxList
represents the component type used to render the <MediaDeliverableCheckBoxList/>
element. Whenever Form
is re-rendered due to props or state changing, React will re-render its children. If the component type of a particular child is the same (plus some other criteria such as key
being the same if specified), then it will update the existing DOM node(s). If the component type of a particular child is different, then the corresponding DOM nodes will be removed and new ones added to the DOM.
通过定义MediaDeliverableCheckBoxList
组件类型中的Form
功能,你是导致该组件类型为每个渲染不同。这将导致替换所有DOM节点,而不仅仅是更新它们,并且当删除之前具有焦点的DOM节点时,将导致焦点消失。这也将导致性能大大降低。
您可以通过以下方式解决此问题:将组件类型移到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}/>
);
}
您CheckboxGroup
可能还有其他组件也遇到同样的问题。
嗨,瑞安,非常感谢您的解释。非常有帮助。我尝试模拟您的代码结构。我将MediaDeliverableCheckBoxList移到了表单函数的外部,就像您的示例一样,但是现在它指向“ values.map”中的句点,并向我显示错误:“解析错误:意外的标记,预期为','“。是否有原因?为什么React无法正确读取此功能?我不确定我为什么会在这里遇到麻烦。
更改后,我需要查看您的确切代码。我认为由于遗漏了诸如FormGroup之类的示例,因此我的示例存在一些轻微的语法问题。如果您将新代码添加到您的问题中,我可以帮助您找到语法问题。
我使用新的form.js更新了原始问题,并添加了我在代码之间被注释的错误。让我知道你的想法...
您只需要重新添加MediaDeliverableCheckBoxList中的FormControl,FormLabel和FormGroup元素。我只是为了消除混乱而删除了它们,但是在此过程中,我在语法
{values.map...}
上不予理--该部分必须位于JSX元素内(例如<FormGroup>
)。我知道了!我重新添加了这些元素,但是唯一缺少的是参数“ values”,该参数未定义,因此我将其添加到MediaDeliverableCheckBoxList JSX组件节点属性中,现在看来工作正常。非常感谢!!!