原文在這裏.java
es6的語法node
class List extends React.Component {
render() {
return (<ul>{this.props.children}</ul>);
}
}
複製代碼
簡單的 javascripg函數也能夠!react
//Stateless function syntax
const List = function(children) {
return (<ul>{children}</ul>);
};
//ES6 arrow syntax
const List = (children) => (<ul>{children}</ul>);
複製代碼
完全的模板,沒有本身任何的數據,也沒有生命週期方法. 純粹依賴於輸入.es6
目的是最爲一個函數接收 app sate 對象
數組
import React from 'react';
import ReactDOM from 'react-dom';
const App = appState => (<div className="container"> <h1>App name</h1> <p>Some children here...</p> </div>);
//這裏定義了渲染的方法,做爲 APP函數的屬性,而且是柯理化的, 等待傳入 dom 元素
App.render = R.curry((node, props) => ReactDOM.render(<App {...props}/>, node)); export default App; 複製代碼
在純函數中,state 必需要在外部管理,而後以 props 的形式傳遞給組件
. 下面看看這個解釋的例子bash
簡單的 timer 組件只接受 secondsElapsed 參數:閉包
import React from 'react';
export default ({ secondsElapsed }) => (<div className="well"> Seconds Elapsed: {secondsElapsed} </div>);
複製代碼
添加到 APP 中app
import React from 'react';
import ReactDOM from 'react-dom';
import R from 'ramda';
import Timer from './timer';
const App = appState => (<div className="container">
<h1>App name</h1>
//Timer 只從父組件接受 props 做爲本身的數據
<Timer secondsElapsed={appState.secondsElapsed} />
</div>);
App.render = R.curry((node, props) => ReactDOM.render(<App {...props}/>, node));
export default App;
複製代碼
最後建立main.js 文件,啓動渲染過程less
import App from './components/app'; //導入容器組件
// 咱們已經有了柯理化的方法
//App.render = R.curry((node, props) => ReactDOM.render(<App {...props}/>, node));
//配置好渲染的目標元素
const render = App.render(document.getElementById('app'));
//state 初始值
let appState = {
secondsElapsed: 0
};
//first render 首次渲染
render(appState);
//屢次重複渲染
setInterval(() => {
appState.secondsElapsed++;
render(appState);
}, 1000);
複製代碼
對於上面的代碼, 變化的是組件的 state, 渲染的目標元素是一直不變的, 因此咱們用柯理化配置好一個工廠函數dom
//閉包再工做!
const render = App.render(document.getElementById(‘app’));
複製代碼
柯理化返回的函數,等待傳入 props
(props) => ReactDOM.render(...)
複製代碼
只要 State發生變化,咱們須要渲染時,只須要傳遞 state 就能夠了
setInterval(() => {
appState.secondsElapsed++;
render(appState);
}, 1000);
複製代碼
每一秒鐘, secondsElapsed 屬性會遞增1, 而後做爲參數傳遞給 render 函數
如今能夠實現 Redux 風格的 reduce 函數, reduce式的函數不能突變當前值
currentState->newState
複製代碼
使用 Radma 的 Lenses 來實現
const secondsElapsedLens = R.lensProp('secondsElapsed');
const incSecondsElapsed = R.over(secondsElapsedLens, R.inc);
setInterval(() => {
appState = incSecondsElapsed(appState);
render(appState);
}, 1000);
複製代碼
首先建立 Lens
:
const secondsElapsedLens = R.lensProp('secondsElapsed');
複製代碼
lens能夠聚焦於給定的屬性,不會針對特定的對象, 因此能夠重用.
View
R.view(secondsElapsedLens, { secondsElapsed: 10 }); //=> 10
複製代碼
Set
R.set(secondsElapsedLens, 11, { secondsElapsed: 10 }); //=> 11
複製代碼
用給定的函數 Set
R.over(secondsElapsedLens, R.inc, { secondsElapsed: 10 }); //=> 11
複製代碼
inSecondElapsed reducer 是一個偏應用函數(partial application), 這一行
const incSecondsElapsed = R.over(secondsElapsedLens, R.inc);
複製代碼
會返回一個新的函數,一旦用appState 調用, 就會應用 R.inc在 lensed prop secondElapsed 上.
appState=incSecondElapsed(appState)
複製代碼
開篇提到,React 組件能夠做爲函數, 那麼能夠用 R.compose來 compose 這些函數嗎? 固然是能夠的
用 React.createClass 是這樣的:
const TodoList = React.createClass({
render: function() {
const createItem = function(item) {
return (<li key={item.id}>{item.text}</li>);
};
return (<div className="panel panel-default"> <div className="panel-body"> <ul> {this.props.items.map(createItem)} </ul> </div> </div>);
}
});
複製代碼
如今問題是: TodoList 能夠由小的可重用部分 composition 而成嗎? 能夠的. 能夠分爲三個更小的組件
const Container = children => (<div className="panel panel-default"> <div className="panel-body"> {children} </div> </div>);
複製代碼
const List = children => (<ul> {children} </ul>);
複製代碼
const ListItem = ({ id, text }) => (<li key={id}> <span>{text}</span> </li>);
複製代碼
如今一步一動,看看每一步的輸出
Container(<h1>Hello World!</h1>);
/**
* <div className="panel panel-default">
* <div className="panel-body">
* <h1>Hello World!</h1>
* </div>
* </div>
*/
Container(List(<li>Hello World!</li>));
/**
* <div className="panel panel-default">
* <div className="panel-body">
* <ul>
* <li>Hello World!</li>
* </ul>
* </div>
* </div>
*/
const TodoItem = {
id: 123,
text: 'Buy milk'
};
Container(List(ListItem(TodoItem)));
/**
* <div className="panel panel-default">
* <div className="panel-body">
* <ul>
* <li>
* <span>Buy milk</span>
* </li>
* </ul>
* </div>
* </div>
*/
複製代碼
若是用 compose 函數,過程以下
R.compose(Container, List)(<li>Hello World!</li>);
/** * <div className="panel panel-default"> * <div className="panel-body"> * <ul> * <li>Hello World!</li> * </ul> * </div> * </div> */
const ContainerWithList = R.compose(Container, List);
R.compose(ContainerWithList, ListItem)({id: 123, text: 'Buy milk'});
/** * <div className="panel panel-default"> * <div className="panel-body"> * <ul> * <li> * <span>Buy milk</span> * </li> * </ul> * </div> * </div> */
const TodoItem = {
id: 123,
text: 'Buy milk'
};
const TodoList = R.compose(Container, List, ListItem);
TodoList(TodoItem);
/** * <div className="panel panel-default"> * <div className="panel-body"> * <ul> * <li> * <span>Buy milk</span> * </li> * </ul> * </div> * </div> */
複製代碼
列表的工廠函數,TodoList 組件能夠看做爲Container,List和 ListItem 的組合 如今 還只能接受一個參數, 須要能夠接受一個數組
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);
/** * <div className="panel panel-default"> * <div className="panel-body"> * <ul> * <li> * <span>One</span> * </li> * <li> * <span>Two</span> * </li> * <li> * <span>Three</span> * </li> * </ul> * </div> * </div> */
複製代碼
//This
return todos.map(function(todo) {
return ListItem(todo);
});
//Is the same as
return todos.map(ListItem);
//So the result would be
const mapTodos = function(todos) {
return todos.map(ListItem);
};
//The same using Ramda
const mapTodos = function(todos) {
return R.map(ListItem, todos);
};
//Now remember two things from Ramda docs:
// - Ramda functions are automatically curried
// - The parameters to Ramda functions are arranged to make it convenient for currying.
// The data to be operated on is generally supplied last.
//So:
const mapTodos = R.map(ListItem);
//At this point mapTodos variable is rendudant, we don't need it anymore:
const TodoList = R.compose(Container, List, R.map(ListItem));
複製代碼
import React from 'React';
import R from 'ramda';
const Container = children => (<div className="panel panel-default"> <div className="panel-body"> {children} </div> </div>);
const List = children => (<ul> {children} </ul>);
const ListItem = ({ id, text }) => (<li key={id}> <span>{text}</span> </li>);
const TodoList = R.compose(Container, List, R.map(ListItem));
export default TodoList;
複製代碼
工廠配置好了,就等數據了
let appState = {
secondsElapsed: 0,
todos: [
{id: 1, text: 'Buy milk'},
{id: 2, text: 'Go running'},
{id: 3, text: 'Rest'}
]
};
複製代碼
import TodoList from './todo-list';
const App = appState => (<div className="container">
<h1>App name</h1>
<Timer secondsElapsed={appState.secondsElapsed} />
<TodoList todos={appState.todos} />
</div>);
複製代碼
TodoList組件期待的參數是一個todos數組,
<TodoList todos={appState.todos} />
//const TodoList = R.compose(Container, List, R.map(ListItem))
複製代碼
React stateless component是做爲函數的,因此咱們也能夠傳遞參數
TodoList({todos: appState.todos});
複製代碼
最好是傳遞單個參數,因此這種狀況,再改進一下
const TodoList = R.compose(Container, List, R.map(ListItem), R.prop('todos'));
複製代碼
調用就直接改成:
TodoList(appState)
複製代碼
結束