模块化和抽象反应组件功能
Posted
技术标签:
【中文标题】模块化和抽象反应组件功能【英文标题】: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 嘿,你能在答案中证明这是什么样子吗?有点困惑你所说的“包装每个复选框”是什么意思。
将数据提取到商店中与所要求的相反 - 与商店相关的任何组件都不能在不同的区域或应用程序中重复使用
【参考方案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);
)
)
【讨论】:
干得好。有一个主要问题是,当我将<Checkbox>
放在表格或段落中或地图找不到的任何地方时。 <Checkbox>
必须是<CheckboxGroup>
正下方的***元素。对于大多数用例,这不起作用。
我已经更新了示例,展示了如何让子元素成为***元素之外的元素。【参考方案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)
【讨论】:
以上是关于模块化和抽象反应组件功能的主要内容,如果未能解决你的问题,请参考以下文章