内容简介:喜欢本文,请置顶或星标
点击上方“ 独立开发者周刊 ”
喜欢本文,请置顶或星标
原文 - Learning React Without Using React Part 2
接着我们上次讲到的内容。 这篇文章将着重于改进我们简单的待办事项列表。 当前的实现由一组函数组成,它们呈现完整的应用,并包含一个管理我们状态的简单store。 但是,我们需要做一些事情来改进我们的应用。 这里是一个与当前示例和代码的链接。
首先,我们没有妥善处理我们的事件。 我们的组件不处理任何事件。 在React中,事件触发数据流。 这意味着我们的组件应该触发事件。 举个例子我们的ItemRow方法应该调用一个通过props传递过来的方法来执行click事件。 我们如何做到这一点?
这是一个初步尝试。
function ItemRow(props) {
var className = props.completed ? 'item completed' : 'item';
return $('<li>')
.on('click', props.onUpdate.bind(null, props.id))
.addClass(className)
.attr('id', props.id)
.html(props.text);
}
我们在列表元素中添加了一个事件监听器,当单击该项目时,它将触发一个点击事件。 onUpdate函数通过props传递过来。
现在我们需要一个为我们创建元素的函数。
function createElement(tag, attrs, children) {
var elem = $('<' + tag + '>');
for (var key in attrs) {
var val = attrs[key];
if (key.indexOf('on') === 0) {
var event = key.substr(2).toLowerCase();
elem.on(event, val)
} else {
elem.attr(key, val);
}
}
return elem.html(children);
}
通过实现createElement函数,我们可以将ItemRow函数重构为如下所示:
function ItemRow(props) {
var className = props.completed ? 'item completed' : 'item';
return createElement('li', {
id: props.id,
class: className,
onClick: props.onUpdate.bind(null, props.id)
}, props.text
);
}
需要注意的是,在React的 createElement中创建的是javaScript对象,是用来表示Dom元素,而不是Dom元素本身。 另一方面,让我们来看看当您编写JSX时实际发生了什么。
以下JSX示例:
return ( <div id='el' className:'entry'>Hello</div>)
被转换为
var SomeElement = React.createElement('div', {id: 'el', className: 'entry'}, 'Hello');
调用SomeElement()将返回这样的对象:
{
// ..
type: 'div',
key: null,
ref: null,
props: {children: 'Hello', className: 'entry', id: 'el' },
}
对于更详细的认识,可以读 React Components, Elements, and Instances.
回到我们的例子。 onUpdate从哪里来?
我们在render中定义了一个updateState函数,并通过props将它传递到ItemList组件。
function render(props, node) {
function updateState(toggleId) {
state.items.forEach(function(el) {
if (el.id === toggleId) {
el.completed = !el.completed;
}
});
store.setState(state);
}
node.empty().append([ItemsList({
items: props.items,
onUpdate: updateState
})]);
}
ItemsList方法本身传递onUpdate给每一个ItemRow
function extending(base, item) {
return $.extend({}, item, base);
}
function ItemsList(props) {
return createElement(‘ul’, {}, props.items
.map(extending.bind(null, {
onUpdate: props.onUpdate
}))
.map(ItemRow));
}
通过采用这种方法,我们实现了以下步骤: 数据流从组件层次结构向下流而响应事件则向上流。 这也意味着我们可以删除先前定义的全局监听器移动到render中,这就是前面提到的updateState。
更多的改进。
让我们用一个方法生成input和button元素。 所以最终我们的标记只包含一个div。
<div id="app"></app>
例如,可以很容易地创建这样的input元素。
var input = createElement(‘input’, { id: ‘input’ });
我们还可以移动一个全局的监听button点击的方法到SearchBar方法中。SearchBar返回一个input和button元素,通过一个来自props中的回调方法来处理点击事件。
function SearchBar(props) {
function onButtonClick(e) {
var val = $('#input').val();
$('#input').val('');
props.update(val);
e.preventDefault();
}
var input = createElement('input', { id: 'input' });
var button = createElement('button',
{ id: 'add', onClick: onButtonClick.bind(null)}, 'Add');
return createElement(‘div’, {}, [input, button]);
}
现在我们的render方法需要调用SearchBar并且传递一个适当的props。 在更新render方法之前,让我们花一点时间来思考下如何重构。 让我们暂时忽略我们的store处理高级别组件中的状态。 直到现在,所有的方法都是无状态的,我们将创建一个方法来处理状态并且在合适的时候更新子组件。
容器组件 让我们创建高级别容器。 也可以阅读 Presentational and Container Components 帮助理解。 下面应该给你一个更好的建议。
所以我们实现容器组件App。 它所做的就是调用SearchBar和ItemList方法,并返回一个元素数组。 我们要重新考虑Render方法。 大部分代码会简单地进入App组件。
在看App组件之前,让我们快速浏览一下我们的render:
function render(component, node) {
node.empty().append(component);
}
render(App(state), $(‘#app’));
render现在仅仅负责将应用呈现在给定的节点中。 React比这个简单的实现要复杂的多。 我们只是将一个元素树附加到一个已定义的根元素中,但它应该足以表达一个高层次的概念。
我们的App组件现在变成了真正的容器组件。
function App(props) {
function updateSearchState(value) {
state.items.push({id:state.id++, text:value, completed:false});
store.setState(state);
}
function updateState(toggleId) {
state.items.forEach(function(el) {
if (el.id === toggleId) {
el.completed = !el.completed;
}
});
store.setState(state);
}
return [SearchBar({update: updateSearchState}),
ItemsList({items: props.items, onUpdate: updateState})];
}
我们还需要再做一件事。 我们正在访问一个全局store,并调用setState来重新render。
让我们重构App组件,使其重新绘制他的子组件而无需调用store。 我们如何做到这一点?
首先,让我们忽略store并找出如何调用setState方法来重新绘制组件和它的子元素。
我们需要在这个高级组件中跟踪当前状态,并且在setState发生变化时也要注意重新绘制DOM。 这里有一个非常原始的方法:
function App(props) {
function getInitialState(props) {
return { items: [], id: 0 };
}
var _state = getInitialState(), _node = null;
function setState(state) {
_state = state;
render();
}
// ...
}
我们通过调用getInitialState来初始化我们的状态,当我们通过setState更新状态时,我们调用在App组件中调用它自己的render方法。
创建属于App组件自己的render方法。 在render方法中要么创建一个根节点,要么在状态更改时简单地更新节点。
// naive implementation of render…
function render() {
var children = [SearchBar({ update: updateSearchState}),
ItemsList({ items: _state.items,
onUpdate: updateState
})];
if (!_node) {
return _node = createElement(‘div’, { class: ‘top’ }, children);
} else {
return _node.html(children);
}
}
这为了表达一个事实,在React组件内调用setState,不绘制完整的应用,只绘制组件和它的子元素,即不调用全局render方法只调用App组件的render方法。
这是更新后的全局render方法的调用。 我们创建App组件时没有任何参数,只是依赖于getInitialState返回初始状态。
function render(component, node) {
node.empty().append(component);
}
render(App(), $(‘#app’));
Check the functioning example including the complete code.
细化
如果我们有一个泛型函数,它会返回一个带有setState函数的对象,并且能够辨别传入的props和组件状态。 这便是一个简单的创建组件的方法。
这样的初版:
var App = createClass({
updateSearchState: function(string) { //... },
updateState: function(obj) { //... },
render: function() {
var children = [SearchBar({ update: this.updateSearchState}),
ItemsList({ items: this.state.items,
onUpdate: this.updateState
})];
return createElement(‘div’, { class: ‘top’ }, children);
}
})
好消息是,React提供了多个选项来创建组件,包括使用React.createClass。 其他选项包括ES6中的class和无状态的函数 for more information consult the docs 。
欢迎扫码关注
独立开发者周刊
↓
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 理解 ARC 实现原理
- 理解 Block 实现原理
- 理解SpringBoot自动配置实现
- 理解并实现 ResNet(Keras)
- React setState源码实现理解
- 透彻理解 NSNotificationCenter 通知(附实现代码)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java数据结构和算法
拉佛 / 计晓云 / 中国电力出版社 / 2004-02-01 / 55.00元
《Java数据结构和算法》(第2版)以一种易懂的方式教授如何安排和操纵数据的问题,其中不乏一些难题:了解这些知识以期使计算机的应用获得最好的表现。不管使用何种语言或平台,掌握了数据结构和算法将改进程序的质量和性能。 《Java数据结构和算法》(第2版)提供了一套独创的可视讨论专题用以阐明主要的论题:它使用Java语言说明重要的概念,而避免了C/C++语言的复杂性,以便集中精力论述数据结构和算法。经......一起来看看 《Java数据结构和算法》 这本书的介绍吧!