亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

React + Ramda: 函數(shù)式編程嘗鮮

tomener / 3551人閱讀

摘要:每當(dāng)?shù)闹蹈淖兒螅覀冎恍枰匦抡{(diào)用方法即可現(xiàn)在,讓我們來實現(xiàn)一個類似風(fēng)格的歸約函數(shù),以不斷的遞增。歸約函數(shù)是不允許修改當(dāng)前狀態(tài)的,所有最簡單的實現(xiàn)方式就是。

原文:Functional Components with React stateless functions and Ramda

閱讀本文需要的知識儲備:

函數(shù)式編程基本概念(組合、柯里化、透鏡)

React 基本知識(組件、狀態(tài)、屬性、JSX)

ES6 基本知識(class、箭頭函數(shù))

React 無狀態(tài)函數(shù)

React 組件最常見的定義方法:

const  List = React.createClass({
  render: function() {
    return (
    {this.props.children}
); } });

或者使用 ES6 類語法:

class List extends React.Component {
  render() {
    return (
    {this.props.children}
); } }

又或者使用普通的 JS 函數(shù):

// 無狀態(tài)函數(shù)語法
const List = function(children) {
  return (
    {children}
); }; //ES6 箭頭函數(shù)語法 const List = (children) => (
    {children}
);

React 官方文檔對這種組件做了以下說明:

這種簡化的組件 API 適用于僅依賴屬性的純函數(shù)組件。這些組件不允許擁有內(nèi)部狀態(tài),不會生成組件實例,也沒有組件的生命周期方法。它們只對輸入進行純函數(shù)轉(zhuǎn)換。不過開發(fā)者仍然可以為它們指定 .propTypes.defaultProps,只需要設(shè)置為函數(shù)的屬性就可以了,就跟在 ES6 類上設(shè)置一樣。

同時也說到:

理想情況下,大部分的組件都應(yīng)該是無狀態(tài)的函數(shù),因為在未來我們可能會針對這類組件做性能優(yōu)化,避免不必要的檢查和內(nèi)存分配。所以推薦大家盡可能的使用這種模式來開發(fā)。

是不是覺得挺有趣的?

React 社區(qū)似乎更加關(guān)注通過 classcreateClass 方式來創(chuàng)建組件,今天讓我們來嘗鮮一下無狀態(tài)組件。

App 容器

首先讓我們來創(chuàng)建一個函數(shù)式 App 容器組件,它接受一個表示應(yīng)用狀態(tài)的對象作為參數(shù):

import React from "react";
import ReactDOM from "react-dom";

const App = appState => (

App name

Some children here...

);

然后,定義一個 render 方法,作為 App 函數(shù)的屬性:

import React from "react";
import ReactDOM from "react-dom";
import R from "ramda";

const App = appState => (

App name

Some children here...

); App.render = R.curry((node, props) => ReactDOM.render(, node)); export default App;

等等!有點看不明白了!
為什么我們需要一個柯里化的渲染函數(shù)?又為什么渲染函數(shù)的參數(shù)順序反過來了?
別急別急,這里唯一要說明的是,由于我們使用的是無狀態(tài)組件,所以狀態(tài)必須由其它地方來維護。也就是說,狀態(tài)必須由外部維護,然后通過屬性的方式傳遞給組件。
讓我們來看一個具體的計時器例子。

無狀態(tài)計時器組件

一個簡單的計時器組件只接受一個屬性 secondsElapsed

import React from "react";

export default ({ secondsElapsed }) => (
Seconds Elapsed: {secondsElapsed}
);

把它添加到 App 中:

import React from "react";
import ReactDOM from "react-dom";
import R from "ramda";
import Timer from "./timer";

const App = appState => (

App name

); App.render = R.curry((node, props) => ReactDOM.render(, node)); export default App;

最后,創(chuàng)建 main.js 來渲染 App

import App from "./components/app";
const render = App.render(document.getElementById("app"));

let appState = {
  secondsElapsed: 0
};

//first render
render(appState);

setInterval(() => {
  appState.secondsElapsed++;
  render(appState);
}, 1000);

在進一步說明之前,我想說,appState.secondElapsed++ 這種修改狀態(tài)的方式讓我覺得非常不爽,不過稍后我們會使用更好的方式來實現(xiàn)。

這里我們可以看出,render 其實就是用新屬性來重新渲染組件的語法糖。下面這行代碼:

const render = App.render(document.getElementById(‘a(chǎn)pp’));

會返回一個具有 (props) => ReactDOM.render(...) 函數(shù)簽名的函數(shù)。
這里并沒有什么太難理解的內(nèi)容。每當(dāng) secondsElapsed 的值改變后,我們只需要重新調(diào)用 render 方法即可:

setInterval(() => {
  appState.secondsElapsed++;
  render(appState);
}, 1000);

現(xiàn)在,讓我們來實現(xiàn)一個類似 Redux 風(fēng)格的歸約函數(shù),以不斷的遞增 secondsElapsed。歸約函數(shù)是不允許修改當(dāng)前狀態(tài)的,所有最簡單的實現(xiàn)方式就是 currentState -> newState。

這里我們使用 Ramda 的透鏡(Lens)來實現(xiàn) incSecondsElapsed 函數(shù):

const secondsElapsedLens = R.lensProp("secondsElapsed");
const incSecondsElapsed = R.over(secondsElapsedLens, R.inc);

setInterval(() => {
  appState = incSecondsElapsed(appState);
  render(appState);
}, 1000);

第一行代碼中,我們創(chuàng)建了一個透鏡:

const secondsElapsedLens = R.lensProp("secondsElapsed");

簡單來說,透鏡是一種專注于給定屬性的方式,而不關(guān)心該屬性到底是在哪個對象上,這種方式便于代碼復(fù)用。當(dāng)我們需要把透鏡應(yīng)用于對象上時,可以有以下操作:

View

R.view(secondsElapsedLens, { secondsElapsed: 10 });  //=> 10

Set

R.set(secondsElapsedLens, 11, { secondsElapsed: 10 });  //=> 11

以給定函數(shù)來設(shè)置

R.over(secondsElapsedLens, R.inc, { secondsElapsed: 10 });  //=> 11

我們實現(xiàn)的 incSecondsElapsed 就是對 R.over 進行局部應(yīng)用的結(jié)果。

const incSecondsElapsed = R.over(secondsElapsedLens, R.inc);

該行代碼會返回一個新函數(shù),一旦調(diào)用時傳入 appState,就會把 R.inc 應(yīng)用在 secondsElapsed 屬性上。

需要注意的是,Ramda 從來都不會修改對象,所以我們需要自己來處理臟活:

appState = incSecondsElapsed(appState);

如果想支持 undo/redo ,只需要維護一個歷史數(shù)組記錄下每一次狀態(tài)即可,或者使用 Redux 。

目前為止,我們已經(jīng)品嘗了柯里化和透鏡,下面讓我們繼續(xù)品嘗組合

組合 React 無狀態(tài)組件

當(dāng)我第一次讀到 React 無狀態(tài)組件時,我就在想能否使用 R.compose 來組合這些函數(shù)呢?答案很明顯,當(dāng)然是 YES 啦:)

讓我們從一個 TodoList 組件開始:

const TodoList = React.createClass({
  render: function() {
    const createItem = function(item) {
      return (
  • {item.text}
  • ); }; return (
      {this.props.items.map(createItem)}
    ); } });

    現(xiàn)在問題來了,TodoList 能否通過組合更小的、可復(fù)用的組件來實現(xiàn)呢?當(dāng)然,我們可以把它分割成 3 個小組件:

    容器

    const Container = children => (
    {children}
    );

    列表

    const List = children => (
      {children}
    );

    列表項

    const ListItem = ({ id, text }) => (
  • {text}
  • );

    現(xiàn)在,我們來一步一步看,請一定要在理解了每一步之后才往下看:

    Container(

    Hello World!

    ); /** *
    *
    *

    Hello World!

    *
    *
    */ Container(List(
  • Hello World!
  • )); /** *
    *
    *
      *
    • Hello World!
    • *
    *
    *
    */ const TodoItem = { id: 123, text: "Buy milk" }; Container(List(ListItem(TodoItem))); /** *
    *
    *
      *
    • * Buy milk *
    • *
    *
    *
    */

    沒有什么太特別的,只不過是一步一步的傳參調(diào)用。

    接著,讓我們來做一些組合的練習(xí):

    R.compose(Container, List)(
  • Hello World!
  • ); /** *
    *
    *
      *
    • Hello World!
    • *
    *
    *
    */ const ContainerWithList = R.compose(Container, List); R.compose(ContainerWithList, ListItem)({id: 123, text: "Buy milk"}); /** *
    *
    *
      *
    • * Buy milk *
    • *
    *
    *
    */ const TodoItem = { id: 123, text: "Buy milk" }; const TodoList = R.compose(Container, List, ListItem); TodoList(TodoItem); /** *
    *
    *
      *
    • * Buy milk *
    • *
    *
    *
    */

    發(fā)現(xiàn)了沒!TodoList 組件已經(jīng)被表示成了 Container、ListListItem 的組合了:

    const TodoList = R.compose(Container, List, ListItem);

    等等!TodoList 這個組件只接受一個 todo 對象,但是我們需要的是映射整個 todos 數(shù)組:

    const mapTodos = function(todos) {
      return todos.map(function(todo) {
        return ListItem(todo);
      });
    };
    const TodoList = R.compose(Container, List, mapTodos);
    const mock = [
      {id: 1, text: "One"},
      {id: 1, text: "Two"},
      {id: 1, text: "Three"}
    ];
    TodoList(mock);
    
    /**
     *  
    *
    *
      *
    • * One *
    • *
    • * Two *
    • *
    • * Three *
    • *
    *
    *
    */

    能否以更函數(shù)式的方式簡化 mapTodos 函數(shù)?

    // 下面的代碼
    return todos.map(function(todo) {
      return ListItem(todo);
    });
    
    // 等效于
    return todos.map(ListItem);
    
    // 所以變成了
    const mapTodos = function(todos) {
      return todos.map(ListItem);
    };
    
    // 等效于使用 Ramda 的方式
    const mapTodos = function(todos) {
      return R.map(ListItem, todos);
    };
    
    // 注意 Ramda 的兩個特點:
    // - Ramda 函數(shù)默認(rèn)都支持柯里化
    // - 為了便于柯里化,Ramda 函數(shù)的參數(shù)進行了特定排列,
    //   待處理的數(shù)據(jù)通常放在最后
    // 因此:
    const mapTodos = R.map(ListItem);
    
    //此時就不再需要 mapTodos 了:
    const TodoList = R.compose(Container, List, R.map(ListItem));

    噠噠噠!完整的 TodoList 實現(xiàn)代碼如下:

    import React from "React";
    import R from "ramda";
    
    const Container = children => (
    {children}
    ); const List = children => (
      {children}
    ); const ListItem = ({ id, text }) => (
  • {text}
  • ); const TodoList = R.compose(Container, List, R.map(ListItem)); export default TodoList;

    其實,還少了一樣?xùn)|西,不過馬上就會加上。在那之前讓我們先來做些準(zhǔn)備:

    添加測試數(shù)據(jù)到應(yīng)用狀態(tài)

    let appState = {
      secondsElapsed: 0,
      todos: [
        {id: 1, text: "Buy milk"},
        {id: 2, text: "Go running"},
        {id: 3, text: "Rest"}
      ]
    };

    添加 TodoListApp

    import TodoList from "./todo-list";
    const App = appState => (

    App name

    );

    TodoList 接受的是一個 todos 數(shù)組,但是這里卻是:

    我們把列表傳遞作為一個屬性,所以等效于:

    TodoList({todos: appState.todos});

    因此,我們必須修改 TodoList,以便讓它接受一個對象并且取出 todos 屬性:

    const TodoList = R.compose(Container, List, R.map(ListItem), R.prop("todos"));

    這里并沒有什么高深技術(shù)。僅僅是從右到左的組合,R.prop("todos") 會返回一個函數(shù),調(diào)用該函數(shù)會返回其作為的參數(shù)對象的 todos 屬性,接著把該屬性值傳遞給 R.map(ListItem),如此往復(fù):)

    以上就是本文的嘗鮮內(nèi)容。希望能對大家有所幫助,這僅僅是我基于 React 和 Ramda 做的一部分實驗。未來,我會努力嘗試覆蓋高階組件和使用 Transducer 來轉(zhuǎn)換無狀態(tài)函數(shù)。

    完整源碼,線上演示代碼(譯者新增)。

    文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/96005.html

    相關(guān)文章

    • 如何優(yōu)雅安全地在深層數(shù)據(jù)結(jié)構(gòu)中取值

      摘要:如果這個結(jié)構(gòu)非常復(fù)雜,那么想要安全優(yōu)雅地取出一個值,也并非簡單。這是為了在對象中相關(guān)取值的過程,需要驗證每一個和的存在性。并且這個數(shù)據(jù)結(jié)構(gòu)必然是動態(tài)生成的,存在有時有時的情況。在測試過程中,很難復(fù)現(xiàn)。 古有趙子龍面對沖鋒之勢,有進無退,陷陣之志,有死無生的局面,能萬軍叢中取敵將首級。在我們的Javascript中,往往用對象(Object)來存儲一個數(shù)據(jù)結(jié)構(gòu)。如果這個結(jié)構(gòu)非常復(fù)雜,那么...

      RobinQu 評論0 收藏0
    • 如何優(yōu)雅安全地在深層數(shù)據(jù)結(jié)構(gòu)中取值

      摘要:如果這個結(jié)構(gòu)非常復(fù)雜,那么想要安全優(yōu)雅地取出一個值,也并非簡單。這是為了在對象中相關(guān)取值的過程,需要驗證每一個和的存在性。并且這個數(shù)據(jù)結(jié)構(gòu)必然是動態(tài)生成的,存在有時有時的情況。在測試過程中,很難復(fù)現(xiàn)。 古有趙子龍面對沖鋒之勢,有進無退,陷陣之志,有死無生的局面,能萬軍叢中取敵將首級。在我們的Javascript中,往往用對象(Object)來存儲一個數(shù)據(jù)結(jié)構(gòu)。如果這個結(jié)構(gòu)非常復(fù)雜,那么...

      liaorio 評論0 收藏0
    • 翻譯連載 | 附錄 C:函數(shù)編程函數(shù)庫-《JavaScript輕量級函數(shù)編程》 |《你不知道的J

      摘要:為了盡可能提升互通性,已經(jīng)成為函數(shù)式編程庫遵循的實際標(biāo)準(zhǔn)。與輕量級函數(shù)式編程的概念相反,它以火力全開的姿態(tài)進軍的函數(shù)式編程世界。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關(guān)于譯者:這是一個流淌著滬江血液的純粹工程:認(rèn)真,是 HTML 最堅實的梁柱;分享,是 CSS 里最閃耀的一瞥;總結(jié),...

      Miracle 評論0 收藏0
    • 前端常用插件、工具類庫匯總

      摘要:頁面調(diào)試騰訊開發(fā)維護的代碼調(diào)試發(fā)布,錯誤監(jiān)控上報,用戶問題定位。同樣是由騰訊開發(fā)維護的代碼調(diào)試工具,是針對移動端的調(diào)試工具。前端業(yè)務(wù)代碼工具庫。動畫庫動畫庫,也是目前通用的動畫庫。 本人微信公眾號:前端修煉之路,歡迎關(guān)注 本篇文章整理自己使用過的和看到過的一些插件和工具,方便日后自己查找和使用。 另外,感謝白小明,文中很多的工具來源于此。 彈出框 layer:http://layer....

      GitCafe 評論0 收藏0

    發(fā)表評論

    0條評論

    tomener

    |高級講師

    TA的文章

    閱讀更多
    最新活動
    閱讀需要支付1元查看
    <