如何在 ReactJS onSubmit() 中从子组件 <select> 获取父组件中的值

How do I get the value in the parent from a child components <select> in ReactJS onSubmit()

我在尝试从 <select> 获取值时遇到问题。呈现它的组件是 DualListBox,它应该将 selectedref 添加到 <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} />
  }
}