如何在 ReactJS onSubmit() 中从子组件 <select> 获取父组件中的值
How do I get the value in the parent from a child components <select> in ReactJS onSubmit()
我在尝试从 <select>
获取值时遇到问题。呈现它的组件是 DualListBox
,它应该将 selected
的 ref
添加到 <select>
标签。
<div className="rdl-selected">
<select className="form-control" name={this.props.name} ref="selected" multiple>
{selected}
</select>
</div>
问题是在 UpdateDialog
的父组件中,当我在 handleSubmit
[=24] 的页面上单击保存时,我无法获取 DOM 的值=]
ReactDOM.findDOMNode(this.refs['selected']).value.trim();
问题:
我错过了什么?我想知道组件是否需要用某些东西包裹起来才能正确 return 值。
更新
添加了@Klassman 指出的用于传递 onChange 的更改并且它有效
这样,传递下来的 onChange 就会用状态值调用。谢谢
** 是否有另一种不在状态内传递值的方法?**
** 为什么我无法通过裁判引用它?**
谢谢
代码
UpdateDialog
'use strict';
const React = require('react');
const ReactDOM = require('react-dom');
const when = require('when');
const client = require('./client');
const follow = require('./follow'); // function to hop multiple links by "rel"
const ListBoxWidget = require('./ListBoxWidget');
const root = '/api';
class UpdateDialog extends React.Component {
constructor(props) {
super(props);
this.state = {selectedListObjects: null, unselectedListObjects: null};
this.handleSubmit = this.handleSubmit.bind(this);
this.onChange = this.onChange.bind(this);
this.setupTestSuite = this.setupTestSuite.bind(this);
}
onChange(selected) {
this.setState({ selected });
}
handleSubmit(e) {
e.preventDefault();
var updatedObject = {};
this.props.attributes.forEach(attribute => {
var rd = ReactDOM.findDOMNode(this.refs[attribute]).value.trim();
console.log("rd for attribute " + attribute);
console.log(this.refs[attribute]);
console.log(rd);
updatedObject[attribute] = rd;
});
console.log("going into test suite ");
console.log(this.props.objectName);
// save the list box value for the test cases
if (this.props.objectName == "Test Suite") {
console.log("going into test suite ");
console.log("this.refs['selected']");
console.log(this.refs.selected);
console.log(this.refs.available);
console.log("this.state.selected");
console.log(this.state.selected);
console.log("?");
var selectedTestCases = ReactDOM.findDOMNode(this.refs['selected']).value.trim();
console.log("selected react dom");
console.log(selectedTestCases);
if (selectedTestCases != null) {
console.log("selected react dom . value");
console.log(selectedTestCases.value);
}
}
this.props.onUpdate(this.props.object, updatedObject);
window.location = "#";
}
componentDidMount() {
console.log("calling componentDidMount in update");
if (this.props.objectName == "Test Suite") {
this.setupTestSuite();
}
}
render() {
var inputs = this.props.attributes.map(attribute =>
<p key={attribute}>
<input type="text" placeholder={attribute}
defaultValue={this.props.object.entity[attribute]}
ref={attribute} className="field" />
</p>
);
var dialogId = "updateObject-" + this.props.object.entity._links.self.href;
return (
<div>
<a href={"#" + dialogId}>Update</a>
<div id={dialogId} className="modalDialog">
<div>
<a href="#" title="Close" className="close">X</a>
<h2>Update an {this.props.objectName}</h2>
<form>
{inputs}
{this.props.objectName == "Test Suite" && this.state.selectedListObjects != null && this.state.unselectedListObjects != null &&
<ListBoxWidget
onChange={this.onChange}
refName='selectedTestCasesRef'
name='selectedTestCases'
unselectedListObjects={this.state.unselectedListObjects}
selectedListObjects={this.state.selectedListObjects} />
}
<button onClick={this.handleSubmit}>Update</button>
</form>
</div>
</div>
</div>
)
}
}
module.exports = UpdateDialog
ListBoxWidget
'use strict';
const React = require('react');
// import components
const DualListBox = require('react-dual-listbox');
class ListBoxWidget extends React.Component {
constructor(props) {
super(props);
this.state = { selected: this.props.selectedListObjects };
this.onChange = this.onChange.bind(this);
}
onChange(selected) {
this.setState({ selected });
this.props.onChange(selected);
}
render() {
return (
<DualListBox
name={this.props.name}
options={this.props.unselectedListObjects}
preserveSelectOrder
selected={this.state.selected}
defaultValue={this.props.selectedListObjects}
onChange={this.onChange} />
)
}
}
module.exports = ListBoxWidget
Package.json
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"react": "^15.0.1",
"react-dom": "^15.0.1",
"rest": "^1.3.2",
"sockjs-client": "^1.0.3",
"stompjs": "^2.3.3",
"webpack": "^1.13.0",
"when": "^3.7.7",
"react-bootstrap": "^0.29.3",
"jquery": "^2.2.3",
"react-dual-listbox": "^0.3.3"
这是来自 react-dual-listbox
的 class
双列表框
import React from 'react';
import Action from './Action';
class DualListBox extends React.Component {
static propTypes = {
name: React.PropTypes.string,
options: React.PropTypes.array,
available: React.PropTypes.array,
selected: React.PropTypes.array,
onChange: React.PropTypes.func,
preserveSelectOrder: React.PropTypes.bool,
};
/**
* @param {Object} props
*
* @returns {void}
*/
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.onDoubleClick = this.onDoubleClick.bind(this);
}
/**
* @param {Object} event
*
* @return {void}
*/
onClick(event) {
const { target } = event;
const { options, onChange } = this.props;
const direction = target.dataset.moveDirection;
const isMoveAll = target.dataset.moveAll;
const selectRef = direction === 'right' ? 'available' : 'selected';
let selected = [];
if (isMoveAll === '1') {
selected = direction === 'right' ? this.makeOptionsSelected(options) : [];
} else {
selected = this.toggleSelected(
this.getSelectedOptions(this.refs[selectRef])
);
}
onChange(selected);
}
/**
* @param {Object} event
*
* @returns {void}
*/
onDoubleClick(event) {
const value = event.target.value;
const selected = this.toggleSelected([value]);
this.props.onChange(selected);
}
/**
* Converts a flat array to a key/value mapping.
*
* @param {Array} options
*
* @returns {Object}
*/
getLabelMap(options) {
let labelMap = {};
options.forEach((option) => {
if (option.options !== undefined) {
labelMap = { ...labelMap, ...this.getLabelMap(option.options) };
} else {
labelMap[option.value] = option.label;
}
});
return labelMap;
}
/**
* Returns the selected options from a given element.
*
* @param {Object} element
*
* @returns {Array}
*/
getSelectedOptions(element) {
return [...element.options]
.filter((option) => option.selected)
.map((option) => option.value);
}
/**
* Make all the given options selected.
*
* @param {Array} options
*
* @returns {Array}
*/
makeOptionsSelected(options) {
let selected = [];
this.filterAvailable(options).forEach((option) => {
if (option.options !== undefined) {
selected = [...selected, ...this.makeOptionsSelected(option.options)];
} else {
selected.push(option.value);
}
});
return [...this.props.selected, ...selected];
}
/**
* Toggle a new set of selected elements.
*
* @param {Array} selected
*
* @returns {Array}
*/
toggleSelected(selected) {
const oldSelected = this.props.selected.slice(0);
selected.forEach((value) => {
const index = oldSelected.indexOf(value);
if (index >= 0) {
oldSelected.splice(index, 1);
} else {
oldSelected.push(value);
}
});
return oldSelected;
}
/**
* Filter options by a filtering function.
*
* @param {Array} options
* @param {Function} filterer
*
* @returns {Array}
*/
filterOptions(options, filterer) {
const filtered = [];
options.forEach((option) => {
if (option.options !== undefined) {
const children = this.filterOptions(option.options, filterer);
if (children.length > 0) {
filtered.push({
label: option.label,
options: children,
});
}
} else if (filterer(option)) {
filtered.push(option);
}
});
return filtered;
}
/**
* Filter the available options.
*
* @param {Array} options
*
* @returns {Array}
*/
filterAvailable(options) {
if (this.props.available !== undefined) {
return this.filterOptions(options, (option) =>
this.props.available.indexOf(option.value) >= 0 &&
this.props.selected.indexOf(option.value) < 0
);
}
// Show all un-selected options
return this.filterOptions(options, (option) =>
this.props.selected.indexOf(option.value) < 0
);
}
/**
* Filter the selected options.
*
* @param {Array} options
*
* @returns {Array}
*/
filterSelected(options) {
if (this.props.preserveSelectOrder) {
return this.filterSelectedByOrder(options);
}
// Order the selections by the default order
return this.filterOptions(options, (option) =>
this.props.selected.indexOf(option.value) >= 0
);
}
/**
* Preserve the selection order. This drops the opt-group associations.
*
* @param {Array} options
*
* @returns {Array}
*/
filterSelectedByOrder(options) {
const labelMap = this.getLabelMap(options);
return this.props.selected.map((selected) => ({
value: selected,
label: labelMap[selected],
}));
}
/**
* @returns {Array}
*/
renderOptions(options) {
return options.map((option, index) => {
if (option.options !== undefined) {
return (
<optgroup key={index} label={option.label}>
{this.renderOptions(option.options)}
</optgroup>
);
}
return (
<option key={index} value={option.value} onDoubleClick={this.onDoubleClick}>
{option.label}
</option>
);
});
}
/**
* @returns {React.Component}
*/
render() {
const { options } = this.props;
const available = this.renderOptions(this.filterAvailable(options));
const selected = this.renderOptions(this.filterSelected(options));
return (
<div className="react-dual-listbox">
<div className="rdl-available">
<select className="form-control" ref="available" multiple>
{available}
</select>
</div>
<div className="rdl-actions">
<div className="rdl-actions-right">
<Action direction="right" isMoveAll onClick={this.onClick} />
<Action direction="right" onClick={this.onClick} />
</div>
<div className="rdl-actions-left">
<Action direction="left" onClick={this.onClick} />
<Action direction="left" isMoveAll onClick={this.onClick} />
</div>
</div>
<div className="rdl-selected">
<select className="form-control" name={this.props.name} ref="selected" multiple>
{selected}
</select>
</div>
</div>
);
}
}
export default DualListBox;
您可以将 onChange
回调从 ListBoxWidget
传递到 UpdateDialog
简化示例:
class UpdateDialog extends React.Component {
onChange (selected) {
// tadaa! `selected` has passed on to this component
this.setState({ selected })
}
handleSubmit () {
// instead of the `ReactDOM.findDOMNode` stuff, just point to your state:
const selected = this.state.selected
// ...
}
render () {
return <ListBoxWidget onChange={this.onChange} />
}
}
class ListBoxWidget extends React.Component {
onChange (selected) {
this.setState({ selected })
this.props.onChange(selected)
}
render () {
return <DualListBox onChange={onChange} />
}
}
我在尝试从 <select>
获取值时遇到问题。呈现它的组件是 DualListBox
,它应该将 selected
的 ref
添加到 <select>
标签。
<div className="rdl-selected">
<select className="form-control" name={this.props.name} ref="selected" multiple>
{selected}
</select>
</div>
问题是在 UpdateDialog
的父组件中,当我在 handleSubmit
[=24] 的页面上单击保存时,我无法获取 DOM 的值=]
ReactDOM.findDOMNode(this.refs['selected']).value.trim();
问题:
我错过了什么?我想知道组件是否需要用某些东西包裹起来才能正确 return 值。
更新 添加了@Klassman 指出的用于传递 onChange 的更改并且它有效
这样,传递下来的 onChange 就会用状态值调用。谢谢
** 是否有另一种不在状态内传递值的方法?** ** 为什么我无法通过裁判引用它?**
谢谢
代码
UpdateDialog
'use strict';
const React = require('react');
const ReactDOM = require('react-dom');
const when = require('when');
const client = require('./client');
const follow = require('./follow'); // function to hop multiple links by "rel"
const ListBoxWidget = require('./ListBoxWidget');
const root = '/api';
class UpdateDialog extends React.Component {
constructor(props) {
super(props);
this.state = {selectedListObjects: null, unselectedListObjects: null};
this.handleSubmit = this.handleSubmit.bind(this);
this.onChange = this.onChange.bind(this);
this.setupTestSuite = this.setupTestSuite.bind(this);
}
onChange(selected) {
this.setState({ selected });
}
handleSubmit(e) {
e.preventDefault();
var updatedObject = {};
this.props.attributes.forEach(attribute => {
var rd = ReactDOM.findDOMNode(this.refs[attribute]).value.trim();
console.log("rd for attribute " + attribute);
console.log(this.refs[attribute]);
console.log(rd);
updatedObject[attribute] = rd;
});
console.log("going into test suite ");
console.log(this.props.objectName);
// save the list box value for the test cases
if (this.props.objectName == "Test Suite") {
console.log("going into test suite ");
console.log("this.refs['selected']");
console.log(this.refs.selected);
console.log(this.refs.available);
console.log("this.state.selected");
console.log(this.state.selected);
console.log("?");
var selectedTestCases = ReactDOM.findDOMNode(this.refs['selected']).value.trim();
console.log("selected react dom");
console.log(selectedTestCases);
if (selectedTestCases != null) {
console.log("selected react dom . value");
console.log(selectedTestCases.value);
}
}
this.props.onUpdate(this.props.object, updatedObject);
window.location = "#";
}
componentDidMount() {
console.log("calling componentDidMount in update");
if (this.props.objectName == "Test Suite") {
this.setupTestSuite();
}
}
render() {
var inputs = this.props.attributes.map(attribute =>
<p key={attribute}>
<input type="text" placeholder={attribute}
defaultValue={this.props.object.entity[attribute]}
ref={attribute} className="field" />
</p>
);
var dialogId = "updateObject-" + this.props.object.entity._links.self.href;
return (
<div>
<a href={"#" + dialogId}>Update</a>
<div id={dialogId} className="modalDialog">
<div>
<a href="#" title="Close" className="close">X</a>
<h2>Update an {this.props.objectName}</h2>
<form>
{inputs}
{this.props.objectName == "Test Suite" && this.state.selectedListObjects != null && this.state.unselectedListObjects != null &&
<ListBoxWidget
onChange={this.onChange}
refName='selectedTestCasesRef'
name='selectedTestCases'
unselectedListObjects={this.state.unselectedListObjects}
selectedListObjects={this.state.selectedListObjects} />
}
<button onClick={this.handleSubmit}>Update</button>
</form>
</div>
</div>
</div>
)
}
}
module.exports = UpdateDialog
ListBoxWidget
'use strict';
const React = require('react');
// import components
const DualListBox = require('react-dual-listbox');
class ListBoxWidget extends React.Component {
constructor(props) {
super(props);
this.state = { selected: this.props.selectedListObjects };
this.onChange = this.onChange.bind(this);
}
onChange(selected) {
this.setState({ selected });
this.props.onChange(selected);
}
render() {
return (
<DualListBox
name={this.props.name}
options={this.props.unselectedListObjects}
preserveSelectOrder
selected={this.state.selected}
defaultValue={this.props.selectedListObjects}
onChange={this.onChange} />
)
}
}
module.exports = ListBoxWidget
Package.json
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"react": "^15.0.1",
"react-dom": "^15.0.1",
"rest": "^1.3.2",
"sockjs-client": "^1.0.3",
"stompjs": "^2.3.3",
"webpack": "^1.13.0",
"when": "^3.7.7",
"react-bootstrap": "^0.29.3",
"jquery": "^2.2.3",
"react-dual-listbox": "^0.3.3"
这是来自 react-dual-listbox
的 class双列表框
import React from 'react';
import Action from './Action';
class DualListBox extends React.Component {
static propTypes = {
name: React.PropTypes.string,
options: React.PropTypes.array,
available: React.PropTypes.array,
selected: React.PropTypes.array,
onChange: React.PropTypes.func,
preserveSelectOrder: React.PropTypes.bool,
};
/**
* @param {Object} props
*
* @returns {void}
*/
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.onDoubleClick = this.onDoubleClick.bind(this);
}
/**
* @param {Object} event
*
* @return {void}
*/
onClick(event) {
const { target } = event;
const { options, onChange } = this.props;
const direction = target.dataset.moveDirection;
const isMoveAll = target.dataset.moveAll;
const selectRef = direction === 'right' ? 'available' : 'selected';
let selected = [];
if (isMoveAll === '1') {
selected = direction === 'right' ? this.makeOptionsSelected(options) : [];
} else {
selected = this.toggleSelected(
this.getSelectedOptions(this.refs[selectRef])
);
}
onChange(selected);
}
/**
* @param {Object} event
*
* @returns {void}
*/
onDoubleClick(event) {
const value = event.target.value;
const selected = this.toggleSelected([value]);
this.props.onChange(selected);
}
/**
* Converts a flat array to a key/value mapping.
*
* @param {Array} options
*
* @returns {Object}
*/
getLabelMap(options) {
let labelMap = {};
options.forEach((option) => {
if (option.options !== undefined) {
labelMap = { ...labelMap, ...this.getLabelMap(option.options) };
} else {
labelMap[option.value] = option.label;
}
});
return labelMap;
}
/**
* Returns the selected options from a given element.
*
* @param {Object} element
*
* @returns {Array}
*/
getSelectedOptions(element) {
return [...element.options]
.filter((option) => option.selected)
.map((option) => option.value);
}
/**
* Make all the given options selected.
*
* @param {Array} options
*
* @returns {Array}
*/
makeOptionsSelected(options) {
let selected = [];
this.filterAvailable(options).forEach((option) => {
if (option.options !== undefined) {
selected = [...selected, ...this.makeOptionsSelected(option.options)];
} else {
selected.push(option.value);
}
});
return [...this.props.selected, ...selected];
}
/**
* Toggle a new set of selected elements.
*
* @param {Array} selected
*
* @returns {Array}
*/
toggleSelected(selected) {
const oldSelected = this.props.selected.slice(0);
selected.forEach((value) => {
const index = oldSelected.indexOf(value);
if (index >= 0) {
oldSelected.splice(index, 1);
} else {
oldSelected.push(value);
}
});
return oldSelected;
}
/**
* Filter options by a filtering function.
*
* @param {Array} options
* @param {Function} filterer
*
* @returns {Array}
*/
filterOptions(options, filterer) {
const filtered = [];
options.forEach((option) => {
if (option.options !== undefined) {
const children = this.filterOptions(option.options, filterer);
if (children.length > 0) {
filtered.push({
label: option.label,
options: children,
});
}
} else if (filterer(option)) {
filtered.push(option);
}
});
return filtered;
}
/**
* Filter the available options.
*
* @param {Array} options
*
* @returns {Array}
*/
filterAvailable(options) {
if (this.props.available !== undefined) {
return this.filterOptions(options, (option) =>
this.props.available.indexOf(option.value) >= 0 &&
this.props.selected.indexOf(option.value) < 0
);
}
// Show all un-selected options
return this.filterOptions(options, (option) =>
this.props.selected.indexOf(option.value) < 0
);
}
/**
* Filter the selected options.
*
* @param {Array} options
*
* @returns {Array}
*/
filterSelected(options) {
if (this.props.preserveSelectOrder) {
return this.filterSelectedByOrder(options);
}
// Order the selections by the default order
return this.filterOptions(options, (option) =>
this.props.selected.indexOf(option.value) >= 0
);
}
/**
* Preserve the selection order. This drops the opt-group associations.
*
* @param {Array} options
*
* @returns {Array}
*/
filterSelectedByOrder(options) {
const labelMap = this.getLabelMap(options);
return this.props.selected.map((selected) => ({
value: selected,
label: labelMap[selected],
}));
}
/**
* @returns {Array}
*/
renderOptions(options) {
return options.map((option, index) => {
if (option.options !== undefined) {
return (
<optgroup key={index} label={option.label}>
{this.renderOptions(option.options)}
</optgroup>
);
}
return (
<option key={index} value={option.value} onDoubleClick={this.onDoubleClick}>
{option.label}
</option>
);
});
}
/**
* @returns {React.Component}
*/
render() {
const { options } = this.props;
const available = this.renderOptions(this.filterAvailable(options));
const selected = this.renderOptions(this.filterSelected(options));
return (
<div className="react-dual-listbox">
<div className="rdl-available">
<select className="form-control" ref="available" multiple>
{available}
</select>
</div>
<div className="rdl-actions">
<div className="rdl-actions-right">
<Action direction="right" isMoveAll onClick={this.onClick} />
<Action direction="right" onClick={this.onClick} />
</div>
<div className="rdl-actions-left">
<Action direction="left" onClick={this.onClick} />
<Action direction="left" isMoveAll onClick={this.onClick} />
</div>
</div>
<div className="rdl-selected">
<select className="form-control" name={this.props.name} ref="selected" multiple>
{selected}
</select>
</div>
</div>
);
}
}
export default DualListBox;
您可以将 onChange
回调从 ListBoxWidget
传递到 UpdateDialog
简化示例:
class UpdateDialog extends React.Component {
onChange (selected) {
// tadaa! `selected` has passed on to this component
this.setState({ selected })
}
handleSubmit () {
// instead of the `ReactDOM.findDOMNode` stuff, just point to your state:
const selected = this.state.selected
// ...
}
render () {
return <ListBoxWidget onChange={this.onChange} />
}
}
class ListBoxWidget extends React.Component {
onChange (selected) {
this.setState({ selected })
this.props.onChange(selected)
}
render () {
return <DualListBox onChange={onChange} />
}
}