原文在这里.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)
复制代码
结束