模块化和抽象反应组件功能
Modularize and abstract react component functionality
我下面有一个工作组件,它允许一个复选框全部和多个复选框。它工作得很好。但是,我讨厌这样的想法,即每次我想使用此功能时,我都必须随身携带所有这些代码。我正在寻找一种反应方式来制作这个模块化?这是
它没有在一个地方模块化 "input checked all" 功能的全部功能。我必须在每次使用时移动 getInitialState 变量和 changeHandlers。
我认为如果 "input checkbox all" 在功能上是原生于 HTML,我们将如何使用它?我们只需要为元素提供属性,它们就会相互引用,所有的处理程序都会在幕后发生,使用起来很简单。我在这个例子中的目标是拥有 HTML 级别的简单性。我在上面显示的代码没有实现这一点,因为它与函数处理程序和状态初始化程序相关联。 React 是否提供了一种对此进行抽象的方法?
下面是我想要的 API 这个组件。
主要问题是:
- 组件功能与父级无关,这意味着父级不需要存储处理程序和状态的信息。
- 代码目前手动跟踪每个复选框的状态,这意味着如果不说明,就无法动态查找 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>
)
}
})
下面的例子是我认为正确的大方向,大意是为相关的框引入一个包装器组件,然后遍历该组件中的children将它们绑定在一起。
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
为了让 children 具有更大的灵活性,您需要使用像这样的要点 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);
})
}
})
我用一些 jQuery 和 lodash 一起破解了这个。
请注意,此示例进入 DOM 以获取所需的数据。 None 这些复选框的状态由组件存储。据我所知,没有真正的 "React" 方法可以做到这一点。 (我很乐意接受建议。)
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)
我下面有一个工作组件,它允许一个复选框全部和多个复选框。它工作得很好。但是,我讨厌这样的想法,即每次我想使用此功能时,我都必须随身携带所有这些代码。我正在寻找一种反应方式来制作这个模块化?这是
它没有在一个地方模块化 "input checked all" 功能的全部功能。我必须在每次使用时移动 getInitialState 变量和 changeHandlers。
我认为如果 "input checkbox all" 在功能上是原生于 HTML,我们将如何使用它?我们只需要为元素提供属性,它们就会相互引用,所有的处理程序都会在幕后发生,使用起来很简单。我在这个例子中的目标是拥有 HTML 级别的简单性。我在上面显示的代码没有实现这一点,因为它与函数处理程序和状态初始化程序相关联。 React 是否提供了一种对此进行抽象的方法?
下面是我想要的 API 这个组件。
主要问题是:
- 组件功能与父级无关,这意味着父级不需要存储处理程序和状态的信息。
- 代码目前手动跟踪每个复选框的状态,这意味着如果不说明,就无法动态查找 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>
)
}
})
下面的例子是我认为正确的大方向,大意是为相关的框引入一个包装器组件,然后遍历该组件中的children将它们绑定在一起。
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
为了让 children 具有更大的灵活性,您需要使用像这样的要点 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);
})
}
})
我用一些 jQuery 和 lodash 一起破解了这个。
请注意,此示例进入 DOM 以获取所需的数据。 None 这些复选框的状态由组件存储。据我所知,没有真正的 "React" 方法可以做到这一点。 (我很乐意接受建议。)
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)