不看React学会React(一)

原文地址:https://medium.com/javascript-inside/learn-the-concepts-part-1-418952d968cb

学习React的时候总会有许多疑惑。接下来的文章会简单介绍React及其底层原理。

你将从(一)(二)中学到什么?对为什么需要React(也许还包括Redux和其他状态管理器)高层次上的理解。

你不需要什么:不需要了解JSX,ES6/ES*,Webpack,热重载,虚拟DOM如何工作,甚至React本身。

首先要做的事:

让我们看一看这段代码TodoMVC code for jQuery.

你会看到一个render()方法,它会在每次事件触发或者数据发生更新时调用。让我们写一个简单的例子,input里值的改变,触发render方法改变DOM。

var state = {value: null};
$('#input').on('keyup', function() {
   state.value = $(this).val().trim();
   render();
});
function render() {
  $('#output').html(state.value);
}
render();

我们用一个名叫state的全局变量来保持同步。这意味着input的更新做了两件事情:1.更新app的state;2.调用render函数。现在,render函数会根据app的state更新DOM。

记住这个例子,我们一会再回头看看。

另外一个想法:

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;

我们写了一堆函数,返回值是根据input值得来的字符串。以相同的参数调用header函数时,它始终返回相同的字符串。如果你曾经对React的无状态函数不知所云,那么这就是一个简单的例子。只不过React的无状态函数返回的是一个React元素而不是个字符串,不过你懂是这个意思就行了。

好了,现在我们可以创建返回字符串的函数了。让我们回头去扩展我们一开始的例子,给它增加一个add按钮和一个items列表。

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();

下面是现在的视图。一个简单的拥有更改items state功能(从active到finished)的todolist。


todolist.png

一组已定义的事件更新state然后调用render函数。render函数创建了item列表,并向list元素加入字符串。为了简化事件和DOM元素的交互,我们加入了state。更新一发生,就出发相应动作,而不必要非得去定义每一个事件、每一个元素以及它们间的关系。这样,我们就简化了复杂的交互。当state发生变化,我们就调用render方法。

这已经很棒了。我们能通过在input框输入标题向list中加入items,同时我们也能通过点击item本身来改变它们的状态。

render方法看起来很乱。我们试着创建一个以input为参数的方法,它返回一个基于参数input的字符串。

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方法拿不到state,取而代之的是一个input,那么我们接下来如何做呢?这很简单,我们将render方法改造一下,让其以props做参数(这就是React Component期望的东西)。

function render(props) {
    $('#list').html(ItemsList({items : props.items}));
}

render方法不需要知道外部的state是什么。这样,只要传进来一个input,我们就简单以其为参数调用render方法就好了,同时也意味着,render会一次又一次返回相同的结果。我们也该牢记,Dom会起到副作用,但我们先不管它。

从render分离出精确的state后,我们就能非常容易的实现Undo/Redo的例子了。现在我们能够创建历史记录,每当改变发生就保存当前的state。

另一个优化是,给render传入根节点,这样就不必在render方法内部定义一个指定的节点了。

function render(props, node) {
    node.html(ItemsList({items : props.items}));
}

现在我们能简单地以传入state和根节点的方式来调用render了。

render(state, $('#list'));

那么,我们如何做到在改变state的时候不显式地调用render呢?

我们创建一个store,只要我们的应用内任何state发生改变,它都会去调用render方法。下面代码是第一次尝试,实现起来很简单,是创建更高级的state容器的好的开始。

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});
        }
    };
}

通过store的setState方法改变state。当state改变时,就触发render方法。

var store = createStore(state);
store.onUpdate('rootRender', function(state) {
    render(state, $('#list')); 
});

请看这里的例子

至此,我们看到了什么?我们看到了单向数据流的原理。我们给render函数传入一个state,state便朝函数继承的方向流去,比如,ItemListItemRow函数传了一个props。看到了创建了一些组件,同时将这些组件组成了更大的玩意。还记得header的例子吗,我们将div和h2方法都写进了header,这使得我们所有的状态改变都得以预测。我们也对state有了一个清晰了认识。

React就是如此做事的,不过是以更高效的方式。composition、通过实现虚拟DOM对render函数进行优化、单向数据流,等等。

让我们来看看React的真实力量:composition、单向数据流、freedom from DSLs、显式地mutation和静态mental model.(译:不明所以)。
Dan Abramov (https://medium.com/@dan_abramov/youre-missing-the-point-of-react-a20e34a51e1a)

我们还有更多要做,包括改造state容器,重构listeners,实现undo/redo和更多有趣的特性。这些都会在在part2中。

更新:Part 2 is online.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342