2015-09-18 24 views
0

我有一个工作组件,允许复选框全部和复选框。它完美的作品。但是我讨厌这个想法,即每次我想要使用这个特性时,都会被困在所有这些代码中。我正在寻找一种方式来应对这种模块化?这是否是模块化和抽象反应组件功能

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

我认为它就像是如果“输入复选框全部”功能在HTML中是原生的,我们将如何使用它?我们只需要为元素提供属性,并且它们会互相引用,并且所有处理程序都会在引擎盖下发生,使用起来会很简单。这个例子的目标是让HTML级别简单。我上面展示的代码没有实现,因为它被绑定到函数处理程序和状态初始值设定项。反应是否提供了一种抽象的方式?

下面是我对这个组件所需的API。

Here's the working example.

的主要问题是:

  • 的组件功能是淡泊的父母,这意味着paren't并不需要存储的处理程序和状态的信息。
  • 该代码当前手动跟踪每个复选框的状态,这意味着无法动态地查找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> 
    ) 
    } 
}) 
+0

现在您有Test组件来管理复选框的状态。最好将该逻辑移入Store。然后将每个复选框包装到一个包装组件中,该组件从商店读取'checked'状态并将其作为道具传递到复选框中。 – oluckyman

+0

@oluckyman嘿,你能证明在答案中看起来像什么吗? “包装每个复选框”有点困惑你的意思。 – ThomasReggi

+0

将数据提取到商店中将与所要求的相反 - 任何链接到商店的组件都不能在不同的区域或应用程序中重复使用 – Glenjamin

回答

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); 
    }) 
    } 
}) 
+0

好的工作。有一个主要问题,当我在表格或段落内或地图无法找到的地方放置“”时。 ''必须是''下的顶级元素。对于大多数用例而言,这是行不通的。 – ThomasReggi

+0

我已更新示例以显示如何让孩子不是顶级元素。 – Glenjamin

0

我砍死了一起使用一些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)