【问题标题】:Modularize and abstract react component functionality模块化和抽象反应组件功能
【发布时间】:2015-12-15 04:07:14
【问题描述】:

我在下面有一个工作组件,它允许所有复选框和复选框。它完美地工作。但是,我讨厌每次想要使用此功能时都被困在携带所有这些代码的想法。我正在寻找一种反应方式来制作这个模块化?这是吗

它不会将“输入检查所有”功能的全部功能模块化在一个地方。我必须在每次使用时移动 getInitialState 变量和 changeHandlers。

我认为如果“输入复选框全部”在功能上是 HTML 中的原生功能,我们将如何使用它?我们只需要为元素提供属性,它们会相互引用,所有的处理程序都会在后台发生,使用起来很简单。我对这个示例的目标是实现 HTML 级别的简单性。我上面展示的代码并没有实现这一点,因为它与函数处理程序和状态初始化程序有关。 react 是否提供了一种抽象方法?

下面是我想要的这个组件的 API。

Here's the working example.

主要问题有:

  • 组件功能与父级无关,这意味着父级不需要存储处理程序和状态的信息。
  • 代码目前手动跟踪每个复选框的状态,这意味着如果不说明,就无法动态查找 DOM 中有多少个复选框。
  • 整体模块化和易用性。

代码如下:

var InputCheckbox = React.createClass({
  getDefaultProps: function () {
    return {
      checked: false
    }
  },
  render: function () {
    return (
    <input
           checked={this.props.checked}
           type='checkbox'
           {...this.props}/>
    )
  }
})

var Test = React.createClass({
  getInitialState: function () {
    return {
      checked: [false, false, false]
    }
  },
  selectAll: function (event) {
    // Set all checked states to true
    this.setState({
      checked: this.state.checked.map(function () {
        return event.target.checked
      })
    })
  },
  handleChange: function (index, event) {
    var checked = this.state.checked
    checked[index] = event.target.checked
    this.setState({
      checked: checked
    })
  },
  render: function () {
    var isAllChecked = this.state.checked.filter(function (c) {
        return c
      }).length === this.state.checked.length

    return (
    <div>
      Select All:
      <InputCheckbox onChange={this.selectAll} checked={isAllChecked}/>
      <br/>
      <InputCheckbox checked={this.state.checked[0]} onChange={this.handleChange.bind(this, 0)}/>
      <br/>
      <InputCheckbox checked={this.state.checked[1]} onChange={this.handleChange.bind(this, 1)}/>
      <br/>
      <InputCheckbox checked={this.state.checked[2]} onChange={this.handleChange.bind(this, 2)}/>
      <br/>
    </div>
    )
  }
})

React.render(<Test/>, document.body)

理想情况下,我可以像这样使用它:

var Checkbox = require('./Checkbox')

var Test = React.createClass({
  render: function () {
    return (
      <div>
        <Checkbox id="alpha"/>
        <Checkbox htmlFor="alpha"/>
        <Checkbox htmlFor="alpha"/>
        <Checkbox htmlFor="alpha"/>
      </div>
    )
  }
})

【问题讨论】:

  • 现在你有了测试组件来管理复选框的状态。最好将该逻辑移至 Store。并将每个复选框包装到一个包装器组件中,该组件从存储中读取checked 状态并将其作为道具传递给复选框。
  • @oluckyman 嘿,你能在答案中证明这是什么样子吗?有点困惑你所说的“包装每个复选框”是什么意思。
  • 将数据提取到商店中与所要求的相反 - 与商店相关的任何组件都不能在不同的区域或应用程序中重复使用

标签: javascript html checkbox reactjs components


【解决方案1】:

下面的例子我认为是正确的大方向,大体的想法是为相关的盒子引入一个包装组件,然后遍历该组件中的孩子将它们捆绑在一起。

var CheckAll = React.createClass({
render() {
  return <input type="checkbox" {...this.props} />
}
});
var Checkbox = React.createClass({
render() {
  return <input type="checkbox" {...this.props} />
}
});
var CheckboxGroup = React.createClass({
setAll(to) {
  var result = {};
  Object.keys(this.props.boxes).forEach(k => result[k] = to)
  this.props.onChange(result);
},
setOne(name, to) {
  var result = {};
  Object.keys(this.props.boxes).forEach(k => result[k] = this.props.boxes[k])
  result[name] = to;
  this.props.onChange(result);
},
enrichChild(child) {
  var boxes = this.props.boxes;
  var all = Object.keys(boxes).every(k => boxes[k]);
  if (child.type == CheckAll) {
    return React.cloneElement(child, { checked: all,
      onChange: () => this.setAll(!all)
    });
  } else if (child.type == Checkbox) {
    var name = child.props.name;
    return React.cloneElement(child, { checked: !!boxes[name],
      onChange: ({target}) => this.setOne(name, target.checked)
    });
  } else {
    return child;
  }
},
render() {
  return (
    <div>
      {React.Children.map(this.props.children, this.enrichChild)}
    </div>
  )
}
});


var Test = React.createClass({
  getInitialState: function () {
    return {
      boxes: {
        a: true,
        b: false,
        c: false,
      }
    }
  },
  render: function () {
    return (
    <div>
      <CheckboxGroup
        boxes={this.state.boxes}
        onChange={boxes => this.setState({boxes})}
      >
        <CheckAll />
        <Checkbox name="a" />
        <Checkbox name="b" />
        <Checkbox name="c" />
      </CheckboxGroup>
    </div>
    )
  }
})

React.render(<Test/>, document.body)

这是一个 jsbin - https://jsbin.com/zomuxolevo/1/edit?js,output

为了让孩子们更灵活,您需要使用类似https://gist.github.com/dandelany/1ff06f4fa1f8d6f89c5e 之类的方法递归地引导他们

var RecursiveChildComponent = React.createClass({
  render() {
    return <div>
      {this.recursiveCloneChildren(this.props.children)}
    </div>
  },
  recursiveCloneChildren(children) {
    return React.Children.map(children, child => {
      if(!_.isObject(child)) return child;
      var childProps = {someNew: "propToAdd"};
      childProps.children = this.recursiveCloneChildren(child.props.children);
      return React.cloneElement(child, childProps);
    })
  }
})

【讨论】:

  • 干得好。有一个主要问题是,当我将&lt;Checkbox&gt; 放在表格或段落中或地图找不到的任何地方时。 &lt;Checkbox&gt; 必须是&lt;CheckboxGroup&gt; 正下方的顶级元素。对于大多数用例,这不起作用。
  • 我已经更新了示例,展示了如何让子元素成为顶级元素之外的元素。
【解决方案2】:

我使用一些 jQuery 和 lodash 一起破解了这个。

Here's the example running.

请注意,此示例进入 DOM 以获取所需的数据。组件不存储这些复选框的状态。据我所知,没有真正的“反应”方式来做到这一点。 (我非常愿意接受建议。)

var Checkbox = React.createClass({
  componentDidMount: function () {
    var component = React.findDOMNode(this)
    var $component = $(component)
    if ($component.attr('id')) {
      var selector = 'input'
      selector += '[data-component=Checkbox]'
      selector += '[for=' + $component.attr('id') + ']'
      var $forComponents = $(selector)
      $component.on('change', function () {
        var value = $component.prop('checked')
        $forComponents.each(function () {
          $forComponent = $(this)
          $forComponent.prop('checked', value)
        })
      })
      $forComponents.on('change', function () {
        var values = $forComponents.map(function () {
          var $forComponent = $(this)
          var value = $forComponent.prop('checked')
          return value
        })
        var simple = _.chain(values).unique().value()
        if (simple.length === 1 && simple[0] === true) {
          $component.prop('checked', true)
        } else {
          $component.prop('checked', false)
        }
      })
    }
  },
  render: function () {
    return (
    <input
       type='checkbox'
       data-component='Checkbox'
       {...this.props}/>
    )
  }
})

var Test = React.createClass({
  render: function () {
    return (
    <div>
      Select All: <Checkbox id='alpha'/><br/>
      <Checkbox htmlFor='alpha'/><br/>
      <Checkbox htmlFor='alpha'/><br/>
      <Checkbox htmlFor='alpha' defaultChecked/><br/>
      <Checkbox htmlFor='alpha'/><br/>
      <Checkbox htmlFor='alpha'/><br/>
    </div>
    )
  }
})

React.render(<Test/>, document.body)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 2018-05-19
    相关资源
    最近更新 更多