实现 React 理解 React(下)

栏目: 服务器 · 发布时间: 6年前

内容简介:喜欢本文,请置顶或星标

点击上方“ 独立开发者周刊

喜欢本文,请置顶或星标

实现 React 理解 React(下)

原文 - 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

欢迎扫码关注

独立开发者周刊

实现 React 理解 React(下)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Advanced Web Metrics with Google Analytics, 2nd Edition

Advanced Web Metrics with Google Analytics, 2nd Edition

Brian Clifton / Sybex / 2010-3-15 / USD 39.99

Valuable tips and tricks for using the latest version of Google Analytics Packed with insider tips and tricks, this how-to guide is fully revised to cover the latest version of Google Analytics and sh......一起来看看 《Advanced Web Metrics with Google Analytics, 2nd Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试