“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情”
贴上源码 👉🏻 github.com/lulusir/cle…
期待宝子们的star⭐️
先上例子🌰
✨ Counter ✨
需求: 展示点击次数,每次点击都会更新
首先我们定义需要展示的状态和点击方法
import React from 'react';
import { usePresenter } from '@clean-js/react-presenter';
import { Presenter } from '@clean-js/presenter';
interface IViewState {
count: number
}
class CounterPresenter extends Presenter<IViewState> {
constructor() {
super();
this.state = {
count: 0,
};
}
increment = () => {
this.setState((s) => {
s.count += 1
});
}
decrement = () =>{
this.setState((s) => {
s.count -= 1
});
}
}
接下来绑定到组件中
const Counter = () => {
const { presenter, state } = usePresenter(CounterPresenter);
return (
<div>
<p>{state.count}</p>
<button
onClick={presenter.increment}
>
increment
</button>
<button
onClick={presenter.decrement}
>
decrement
</button>
</div>
);
};
export default Counter;
在线地址:链接https://lulusir.github.io/clean-js/demos/counter
✨ TodoList ✨
接下来又是我们的老演员Todolist了
需求:
- 需要3个list来分别展示 Todo列表,分别是:待完成列表,已完成列表,删除列表
- 需要一个输入框和按钮用来添加Todo
- 需要一个按钮来随机完成Todo(打个响指✌🏻)
声明Todo的Entity接口
export interface Todo {
status: 'done' | 'default' | 'delete';
id: number;
content: string;
}
.
实现Presenter,提供数据和方法
import { Presenter, injectable } from '@clean-js/presenter';
import { Todo } from './todo.entity';
/**
* 定义页面需要展示的内容
* loading
* 三种类型的Todos
*/
interface IViewState {
data: Todo[];
}
export class TodoPresenter extends Presenter<IViewState> {
constructor() {
super();
this.state = { data: [] };
}
// 添加一个todo,
add(content: string) {
this.setState((s) => {
s.data.push({ content, id: Math.random(), status: 'default' });
});
}
initTotoList() {
for (let i = 0; i < 3; i++) {
const content = `content ${i}`;
this.add(content);
}
}
finish(id: number) {
this.setState((s) => {
const t = s.data.find((v) => v.id === id);
if (t) {
if (t.status === 'default') {
t.status = 'done';
} else if (t.status === 'done') {
t.status = 'delete';
}
}
});
}
/**
* 打个响指 完成一半
*/
snapFingers() {
this.defaultList().forEach((v, i) => {
if (i % 2 === 0) {
this.finish(v.id);
}
});
}
defaultList() {
return this.state.data.filter((v) => v.status === 'default');
}
doneList() {
return this.state.data.filter((v) => v.status === 'done');
}
deleteList() {
return this.state.data.filter((v) => v.status === 'delete');
}
}
编写界面
在React的函数组件中, 可以用usePresenter来获取TodoPresenter实例
接下来就是把方法和需要渲染的数据绑定到组件中,一个简单的Todolist就完成了👍🏻
import React, { useEffect, useRef } from 'react';
import { Spin, List, Button, Space } from 'antd';
import { usePresenter } from '@clean-js/react-presenter';
import { TodoPresenter } from './index.presenter';
const Index = () => {
const { presenter } = usePresenter(TodoPresenter);
const refInput = useRef<HTMLInputElement>(null);
useEffect(() => {
presenter.initTotoList();
}, []);
return (
<Spin spinning={false}>
<div style={{ marginBottom: '20px' }}>
<input type="text" ref={refInput} />
<Button
style={{ marginLeft: '20px' }}
onClick={() => {
if (refInput.current) {
const content = refInput.current.value;
if (content) {
presenter.add(content);
}
}
}}
>
add todo
</Button>
<Button
style={{ marginLeft: '20px' }}
onClick={() => {
presenter.snapFingers();
}}
>
Snap your Fingers
</Button>
</div>
<Space direction="vertical" size="middle" style={{ display: 'flex' }}>
<List
size="small"
header={'Todo list'}
bordered
dataSource={presenter.defaultList()}
renderItem={(item) => (
<List.Item>
{item.content}
<Button
style={{ marginLeft: '20px' }}
onClick={() => {
presenter.finish(item.id);
}}
>
finish
</Button>
</List.Item>
)}
/>
<List
size="small"
header={'Done list'}
bordered
dataSource={presenter.doneList()}
renderItem={(item) => (
<List.Item>
{item.content}
<Button
danger
style={{ marginLeft: '20px' }}
onClick={() => {
presenter.finish(item.id);
}}
>
delete
</Button>
</List.Item>
)}
/>
<List
size="small"
header={'Delete list'}
bordered
dataSource={presenter.deleteList()}
renderItem={(item) => (
<List.Item>
<del>{item.content}</del>
</List.Item>
)}
/>
</Space>
</Spin>
);
};
export default () => <Index />;
✨ API ✨
Presenter
Presenter是一个状态管理基类,提供了stateState方法和state属性让你设置,修改状态。
- 声明State的接口
- 继承这个Presenter这个基类,并初始化状态
Method
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
state | getter 返回 state | IViewState | |
setState | 设置state,基于immer,在setSatate可以用immer的语法来修改状态 | IViewState |
import { Presenter, injectable } from '@clean-js/presenter';
import { Todo } from './todo.entity';
/**
* 定义页面需要展示的内容
* loading
* 三种类型的Todos
*/
interface IViewState {
data: Todo[];
}
class TodoPresenter extends Presenter<IViewState> {
constructor() {
super();
this.state = { data: [] };
}
.
.
.
✨ View adaptor ✨
视图适配器,在React/Vue 组件中,通过usePresenter来获取Presenter实例
使用如下:
- 使用 usePresenter hook
- 注入 Presenter 类
- 获取 presenter 实例, state 对象
- 在 presenter 使用 setState 方法可以更新 state,并且默认会更新视图
import { usePresenter } from '@clean-js/react-presenter';
.
.
.
const Counter = () => {
const { presenter, state } = usePresenter(CounterPresenter);
return (
<div>
<p>{state.count}</p>
<button
onClick={presenter.increment}
>
increment
</button>
<button
onClick={presenter.decrement}
>
decrement
</button>
</div>
);
};
✨ 调试 ✨
方式1 👉 redux-devtool
- 安装chrome插件redux-devtool
- 开启devtool
import { entry } from '@clean-js/presenter';
if (isLocal) {
entry.showDevtool() // 开启devtool
}
方式2 👉🏻 console.log
if (isLocal) {
entry.showLog() // 开启log,通过console.log输出日志,可以用于小程序环境
}
下一篇会接着晚上TodoList,添加更多的功能,敬请期待
贴上源码 👉🏻 github.com/lulusir/cle…
期待宝子们的star⭐️
整洁架构篇:juejin.cn/post/714092…
其他文章
什么?在React中也可以使用vue响应式状态管理
clean-js | 自从写了这个辅助库,我已经很久没有加过班了…
clean-js | 在hooks的时代下,使用class管理你的状态
clean-js | 手把手教你写一个羊了个羊麻将版
写给前端的数据库入门 | 序
写给前端的数据库入门 | docker & 数据库
有没有一种可能,你从来都没有真正理解async
三分钟实现前端写JAVA这件事——装环境
三分钟实现前端写JAVA这件事——VS code