原文連接 - 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…)
咱們能作的還有不少,包括創建一個改進的狀態容器, 重構監聽器,實現撤銷/重作和更多好的功能。