原文连接 - Learning React Without Using React Part 1javascript
首先了解概念而后学习框架html
当第一次接触React会有不少的困惑。文章将介绍React和它的基本理念。java
经过阅读文章你将会对于为何须要React和Redux或者其余状态容器有一个更好的理解。node
学习时你不须要使用 JSX,ES6/ES*, Webpack,热加载,理解虚拟DOM,甚至不须要使用React本身。react
首先:jquery
让咱们看一下 TodoMVC code for jQuery。git
您将注意到有一个方法render(),每当事件被触发或数据被更新时,它就会被调用。让咱们构建一个例子,当改变一个输入值,触发render方法更新DOM。github
var state = {value: null};
$('#input').on('keyup', function() {
state.value = $(this).val().trim();
render();
});
function render() {
$('#output').html(state.value);
}
render();
复制代码
咱们用一个全局状态state变量来保存同步全部的值。在一次输入后作了两件事:数组
记住这个例子。咱们立刻就会讲到。架构
下面是另外一个须要考虑的地方:
function output(text) {
return '<div>' + text + '</div>';
}
复制代码
调用output('foo') 将返回 '<div>foo</div>'。
如今考虑下面的例子:
function h2(text) {
return '<h2>' + text + '</h2>';
}
function div(text) {
return '<div>' + text + '</div>';
}
function header(text) {
return div(h2(text));
}
console.log(header('foo') === '<div><h2>foo</h2></div>'); //true;
复制代码
咱们编写的方法的返回值基于一个字符串参数。用一样的参数调用header方法老是返回一个一样的元素字符串。若是你曾经在React中考虑过无状态组件的话,这是一个简化版本。React中无状态组件将返回一个React元素而不是一个简单的元素字符串。
咱们知道咱们能够建立返回元素字符串的函数。让咱们回到最初的示例,并扩展它显示add按钮和项目列表。
var state = {items: [], id: 0};
$('#add').on('click', function (e) {
var value = $('#input').val().trim();
$('#input').val('');
state.items.push({id: state.id++, text: value, completed: false});
render();
});
$('#list').on('click', '.item', function () {
var toggleId = parseInt($(this).attr('id'));
state.items.forEach(function (el) {
if (el.id === toggleId) {
el.completed = !el.completed;
}
});
render();
});
function render() {
var items = state.items.map(function (item) {
var completed = item.completed ? 'completed' : '';
return '<li class="item + ' + completed + '" id="' + item.id + '">(' + item.id + ') ' + item.text + '</li>';
}).join('');
var html = '<ul>' + items + '</ul>';
$('#list').html(html);
}
render();
复制代码
咱们有一个简单的待办列表,包括切换项目状态(待办或完成)的能力。
用一组已定义的事件函数更新全局状态state而后调用咱们的render方法。而后render方法建立项目列表而且添加项目到项目列表中。咱们添加了状态来简化事件和元素之间的交互。而不是定义每一个事件和元素以及它们各自的关系,咱们老是在一个行为被触发后当即更新。它简化了处理复杂的交互。当状态改变时,咱们老是调用render方法。
这已经颇有效了。咱们能够经过输入框输入标题来添加条目,咱们能够经过点击条目自己来切换条目状态。
如今render方法看起来有些乱。让咱们尝试建立一个基于传入的props来返回元素字符串的方法。
function ItemRow(props) {
var className = props.completed? ' item completed' : 'item';
return '<li class="' + className +'">' + props.text + '</li>';
}
function ItemsList(props) {
return '<ul>' + props.items.map(ItemRow).join('') + '</ul>';
}
复制代码
咱们已经清理了render函数。
function render() {
$('#list').html(ItemsList({items : state.items}));
}
复制代码
若是render自身不知道状态,而且指望使用一个输入的参数代替呢?这很容易实现,咱们能够用props简单的重构render方法。(这就是React组件所指望的。)
function render(props) {
$('#list').html(ItemsList({items : props.items}));
}
复制代码
render不须要知道外部状态。这使咱们可以简单地调用render方法传入任何已定义的状态。这也意味着传入的状态不改变那么从新绘制将一次又一次地返回相同的结果。虽然咱们应该记住,这是Dom的一个反作用,但让咱们暂时忽略这个事实。
经过将显式状态与绘制部分分离,咱们能够轻松实现撤销/重作。这意味着咱们能够建立一个历史记录并在每次更改时保存。
另外一个优化是将根节点做为参数传入,而不是在render函数中显式地定义节点。
function render(props, node) {
node.html(ItemsList({items : props.items}));
}
复制代码
所以,如今咱们能够简单地调用render方法而且传入已定义的状态和根节点。
render(state, $('#list'));
复制代码
可是,在更新状态以后,如何不须要显式地调用render方法呢?
让咱们构建一个store,当状态从应用的任何地方更新时,只需调用咱们的render方法。这是第一次尝试。虽然这个实现是很是基础的,这是建立一个更高级的状态容器的好起点。
function createStore(initialState) {
var _state = initialState || {}, _listeners = [];
function updateListeners(state) {
_listeners.forEach(function(listener) {
listener.cb(state);
});
}
return {
setState: function(state) {
_state = state;
updateListeners(state);
},
getState: function() {
return _state;
},
onUpdate: function(name, cb) {
_listeners.push({name: name, cb: cb});
}
};
}
复制代码
咱们能够简单地经过存储setState方法更新状态。一旦状态改变后,咱们的render函数会被调用。
var store = createStore(state);
store.onUpdate('rootRender', function(state) {
render(state, $('#list'));
});
复制代码
咱们如今看到了什么?
咱们已经看到单向数据流的简单原理。咱们将状态传递给render函数,状态沿着函数的层次结构传入。例如ItemList传递一个适当的props给ItemRow。咱们已经建立了组件,并将这些组件组合成更大的组件。记得标题的例子,咱们将div和h2函数组合到header中。这里咱们处理的是纯函数。这使得咱们全部的更新均可以预测。咱们如今对咱们的状态也有一个清晰的认识。
在React中,这会以一种很是有效的方式进行。经过实现虚拟DOM、单向数据流等实现架构,优化渲染。
...咱们能够专一于研究React真正的优点:架构,单向数据流,从DSLs中解脱,明确的预期和静态思想模型。
Dan Abramov(medium.com/@dan_abramo…)
咱们能作的还有不少,包括创建一个改进的状态容器, 重构监听器,实现撤销/重作和更多好的功能。