函数式编程
React 思维模式:函数式编程
渲染函数
与
<script>
export default {
components: {},
data() {
return {
notes: [],
};
},
created() {
this.fetchNotes();
},
methods: {
addNote(title, body, createdAt, flagged) {
return database('notes').insert({ title, body, created_at: createdAt, flagged });
},
};
</script>
<template>
<div class="app">
<header-menu
:addNote='addNote'
>
</div>
</template>
<style scoped>
.app {
width: 100%;
height: 100%;
postion: relative;
}
</style>
当我们将视角转回到
View = f(Data)
这种对用户界面的抽象方式确实令笔者耳目一新,这样我们对于界面的组合搭配就可以抽象为对于函数的组合,某个复杂的界面可以解构为数个不同的函数调用的组合变换。
在正式学习
Transformation( 转换)
function NameBox(name) {
return { fontWeight: 'bold', labelContent: name };
}
'Sebastian Markbåge' ->
{ fontWeight: 'bold', labelContent: 'Sebastian Markbåge' };
这样可以极大地方便对于
Abstraction( 抽象)
对于一个复杂的
function FancyUserBox(user) {
return {
borderStyle: "1px solid blue",
childContent: ["Name: ", NameBox(user.firstName + " " + user.lastName)]
};
}
{ firstName: 'Sebastian', lastName: 'Markbåge' } ->
{
borderStyle: '1px solid blue',
childContent: [
'Name: ',
{ fontWeight: 'bold', labelContent: 'Sebastian Markbåge' }
]
};
Composition( 组合)
为了达到真正意义上的重用目标,并不仅仅就是把那个叶子组件组合进一个新的容器,我们也需要在容器中构建出能够组合其他抽象组件的抽象组件。这里我认为的组合要点在于如何把两个或者更多的抽象组件合并成一个新的:
function FancyBox(children) {
return {
borderStyle: '1px solid blue',
children: children
};
}
function UserBox(user) {
return FancyBox([
'Name: ',
NameBox(user.firstName + ' ' + user.lastName)
]);
}
State
一个
function FancyNameBox(user, likes, onClick) {
return FancyBox([
'Name: ', NameBox(user.firstName + ' ' + user.lastName),
'Likes: ', LikeBox(likes),
LikeButton(onClick)
]);
}
// Implementation Details
var likes = 0;
function addOneMoreLike() {
likes++;
rerender();
}
// Init
FancyNameBox(
{ firstName: 'Sebastian', lastName: 'Markbåge' },
likes,
addOneMoreLike
);
注意,这里的例子还是用了带副作用的函数来更新状态,不过我本意是想采用纯函数,即每次返回最新的状态来完成这个工作。我会在下面的例子里阐述这个观点。
Memoization
纯函数的一个好处就是其结果是可以缓存的,这就避免了重复调用带来的性能浪费。我们可以创建一个自带缓存的函数来记录最后调用的参数与返回值,这样我们可以自动地在相同参数的情况下直接返回:
function memoize(fn) {
var cachedArg;
var cachedResult;
return function(arg) {
if (cachedArg === arg) {
return cachedResult;
}
cachedArg = arg;
cachedResult = fn(arg);
return cachedResult;
};
}
var MemoizedNameBox = memoize(NameBox);
function NameAndAgeBox(user, currentTime) {
return FancyBox([
'Name: ',
MemoizedNameBox(user.firstName + ' ' + user.lastName),
'Age in milliseconds: ',
currentTime - user.dateOfBirth
]);
}
Lists
大部分的
function UserList(users, likesPerUser, updateUserLikes) {
return users.map(user => FancyNameBox(
user,
likesPerUser.get(user.id),
() => updateUserLikes(user.id, likesPerUser.get(user.id) + 1)
));
}
var likesPerUser = new Map();
function updateUserLikes(id, likeCount) {
likesPerUser.set(id, likeCount);
rerender();
}
UserList(data.users, likesPerUser, updateUserLikes);
注意,在这个函数里我们传入了多个值,这样就不能缓存结果了。
## Continuations
function FancyUserList(users) {
return FancyBox(
UserList.bind(null, users)
);
}
const box = FancyUserList(data.users);
const resolvedChildren = box.children(likesPerUser, updateUserLikes);
const resolvedBox = {
...box,
children: resolvedChildren
};
State Map
早前我们就知道著名的
function FancyBoxWithState(
children,
stateMap,
updateState
) {
return FancyBox(
children.map(child => child.continuation(
stateMap.get(child.key),
updateState
))
);
}
function UserList(users) {
return users.map(user => {
continuation: FancyNameBox.bind(null, user),
key: user.id
});
}
function FancyUserList(users) {
return FancyBoxWithState.bind(null,
UserList(users)
);
}
const continuation = FancyUserList(data.users);
continuation(likesPerUser, updateUserLikes);
## Memoization Map
上面提到过,当存在多个输入参数的情况下要再想进行缓存就会麻烦一点,我们要使用一些复杂的缓存策略来平衡内存使用与频次。幸运的是很多地方
function memoize(fn) {
return function(arg, memoizationCache) {
if (memoizationCache.arg === arg) {
return memoizationCache.result;
}
const result = fn(arg);
memoizationCache.arg = arg;
memoizationCache.result = result;
return result;
};
}
function FancyBoxWithState(
children,
stateMap,
updateState,
memoizationCache
) {
return FancyBox(
children.map(child => child.continuation(
stateMap.get(child.key),
updateState,
memoizationCache.get(child.key)
))
);
}
const MemoizedFancyNameBox = memoize(FancyNameBox);
## Algebraic Effects
如果我们在一个嵌套多层的
function ThemeBorderColorRequest() { }
function FancyBox(children) {
const color = raise new ThemeBorderColorRequest();
return {
borderWidth: '1px',
borderColor: color,
children: children
};
}
function BlueTheme(children) {
return try {
children();
} catch effect ThemeBorderColorRequest -> [, continuation] {
continuation('blue');
}
}
function App(data) {
return BlueTheme(
FancyUserList.bind(null, data.users)
);
}