持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
前言
哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第十三天,昨天我们深入了解了mobx,今天会给大家介绍一下mobx的应用,看看mobx在React中到底有哪些作用,让我们探寻其中的奥秘。话不多说,开始我们的冒险之旅吧!
1.mobx应用
- mobx-react 核心是将render方法包装为autorun
- 谁用到了可观察属性,就需要被observer修饰,按需渲染
cnpm i react react-dom mobx-react -S
1.1 计数器
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { observable, action } from 'mobx';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
class Store {
@observable number = 0;
@action.bound add() {
this.number++;
}
}
let store = new Store();
@observer
class Counter extends Component {
render() {
return (
<div>
<p>{store.number}</p>
<button onClick={store.add}>+</button>
</div>
)
}
}
ReactDOM.render(<Counter />, document.querySelector('#root'));
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { observable, action } from 'mobx';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
class Store {
@observable counter = { number: 0 };
@action.bound add() {
this.counter.number++;
}
}
let store = new Store();
@observer
class Counter extends Component {
render() {
return (
<div>
<p>{this.props.counter.number}</p>
<button onClick={this.props.add}>+</button>
</div>
)
}
}
ReactDOM.render(<Counter
counter={store.counter}
add={store.add}
/>, document.querySelector('#root'));
1.2 TODO
import React, { Component, Fragment } from 'react';
import ReactDOM from 'react-dom';
import { observable, action, computed } from 'mobx';
import PropTypes from 'prop-types';
import { observer, PropTypes as ObservablePropTypes } from 'mobx-react';
class Todo {
id = Math.random();
@observable text = '';
@observable completed = false;
constructor(text) {
this.text = text;
}
@action.bound toggle() {
this.completed = !this.completed;
}
}
class Store {
@observable todos = [];
@computed get left() {
return this.todos.filter(todo => !todo.completed).length;
}
@computed get filterTodos() {
return this.todos.filter(todo => {
switch (this.filter) {
case 'completed':
return todo.completed;
case 'uncompleted':
return !todo.completed;
default:
return true;
}
});
}
@observable filter = 'all';
@action.bound changeFilter(filter) {
this.filter = filter;
console.log(this.filter);
}
@action.bound addTodo(text) {
this.todos.push(new Todo(text));
}
@action.bound removeTodo(todo) {
this.todos.remove(todo);
}
}
@observer
class TodoItem extends Component {
static porpTypes = {
todo: PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
completed: PropTypes.bool.isRequired
}).isRequired
}
render() {
let { todo } = this.props;
return (
<Fragment>
<input
type="checkbox"
onChange={todo.toggle}
checked={todo.completed} />
<span className={todo.completed ? 'completed' : ''}>{todo.text}</span>
</Fragment>
)
}
}
@observer
class TodoList extends Component {
static propsTypes = {
store: PropTypes.shape({
addTodo: PropTypes.func,
todos: ObservablePropTypes.observableArrayOf(ObservablePropTypes.observableObject)
}).isRequired
};
state = { text: '' }
handleSubmit = (event) => {
event.preventDefault();
this.props.store.addTodo(this.state.text);
this.setState({ text: '' });
}
handleChange = (event) => {
this.setState({ text: event.target.value });
}
render() {
let { filterTodos, left, removeTodo, filter, changeFilter } = this.props.store;
return (
<div className="todo-list">
<form onSubmit={this.handleSubmit}>
<input placeholder="请输入待办事项" type="text" value={this.state.text} onChange={this.handleChange}/>
</form>
<ul>
{
filterTodos.map(todo => (
<li key={todo.id}>
<TodoItem todo={todo} />
<button onClick={() => removeTodo(todo)}>X</button>
</li>
))
}
</ul>
<p>
<span>你还有{left}件待办事项!</span>
<button
onClick={() => changeFilter('all')}
className={filter === 'all' ? 'active' : ''}>全部</button>
<button onClick={() => changeFilter('uncompleted')}
className={filter === 'uncompleted' ? 'active' : ''}>未完成</button>
<button
onClick={() => changeFilter('completed')}
className={filter === 'completed' ? 'active' : ''}>已完成</button>
</p>
</div>
)
}
}
let store = new Store();
ReactDOM.render(<TodoList store={store} />, document.querySelector('#root'));
2.优化
2.1 observe
constructor() {
observe(this.todos, change => {
console.log(change);
this.disposers.forEach(disposer => disposer());
this.disposers = [];
for (let todo of change.object) {
this.disposers.push(observe(todo, change => {
this.save();
//console.log(change)
}));
}
this.save();
});
}
2.2 spy
spy(event => {
//console.log(event);
})
2.3 toJS
constructor() {
observe(this.todos, change => {
console.log(change);
this.disposers.forEach(disposer => disposer());
this.disposers = [];
for (let todo of change.object) {
this.disposers.push(observe(todo, change => {
this.save();
//console.log(change)
}));
}
this.save();
});
}
save() {
localStorage.setItem('todos', JSON.stringify(toJS(this.todos)));
}
3.优化TODO
- 把视图拆解的更细致
- 使用专门的视图渲染列表数据
- 尽可能晚的解构使用数据
import React, { Component, Fragment } from 'react';
import ReactDOM from 'react-dom';
import { trace, observable, action, computed, observe, spy, toJS } from 'mobx';
import PropTypes from 'prop-types';
import { observer, PropTypes as ObservablePropTypes } from 'mobx-react';
spy(event => {
//console.log(event);
})
class Todo {
id = Math.random();
@observable text = '';
@observable completed = false;
constructor(text) {
this.text = text;
}
@action.bound toggle() {
this.completed = !this.completed;
}
}
class Store {
disposers = [];
constructor() {
observe(this.todos, change => {
console.log(change);
this.disposers.forEach(disposer => disposer());
this.disposers = [];
for (let todo of change.object) {
this.disposers.push(observe(todo, change => {
this.save();
//console.log(change)
}));
}
this.save();
});
}
save() {
localStorage.setItem('todos', JSON.stringify(toJS(this.todos)));
}
@observable todos = [];
@computed get left() {
return this.todos.filter(todo => !todo.completed).length;
}
@computed get filterTodos() {
return this.todos.filter(todo => {
switch (this.filter) {
case 'completed':
return todo.completed;
case 'uncompleted':
return !todo.completed;
default:
return true;
}
});
}
@observable filter = 'all';
@action.bound changeFilter(filter) {
this.filter = filter;
console.log(this.filter);
}
@action.bound addTodo(text) {
this.todos.push(new Todo(text));
}
@action.bound removeTodo(todo) {
this.todos.remove(todo);
}
}
@observer
class TodoItem extends Component {
static porpTypes = {
todo: PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
completed: PropTypes.bool.isRequired
}).isRequired
}
render() {
trace();
let { todo } = this.props;
return (
<Fragment>
<input
type="checkbox"
onChange={todo.toggle}
checked={todo.completed} />
<span className={todo.completed ? 'completed' : ''}>{todo.text}</span>
</Fragment>
)
}
}
@observer
class TodoFooter extends Component {
static propTypes = {
};
render() {
trace();
let { left, filter } = this.props.store;
return (
<div>
<span>你还有{left}件待办事项!</span>
<button
onClick={() => changeFilter('all')}
className={filter === 'all' ? 'active' : ''}>全部</button>
<button onClick={() => changeFilter('uncompleted')}
className={filter === 'uncompleted' ? 'active' : ''}>未完成</button>
<button
onClick={() => changeFilter('completed')}
className={filter === 'completed' ? 'active' : ''}>已完成</button>
</div>
)
}
}
@observer
class TodoViews extends Component {
render() {
return (
<ul>
{
this.props.store.filterTodos.map(todo => (
<li key={todo.id}>
<TodoItem todo={todo} />
<button onClick={() => removeTodo(todo)}>X</button>
</li>
))
}
</ul>
)
}
}
@observer
class TodoHeader extends Component {
state = { text: '' }
handleSubmit = (event) => {
event.preventDefault();
this.props.store.addTodo(this.state.text);
this.setState({ text: '' });
}
handleChange = (event) => {
this.setState({ text: event.target.value });
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input placeholder="请输入待办事项" type="text" value={this.state.text} onChange={this.handleChange}/>
</form>
)
}
}
@observer
class TodoList extends Component {
static propsTypes = {
store: PropTypes.shape({
addTodo: PropTypes.func,
todos: ObservablePropTypes.observableArrayOf(ObservablePropTypes.observableObject)
}).isRequired
};
render() {
trace();
return (
<div className="todo-list">
<TodoHeader store={this.props.store} />
<TodoViews store={this.props.store} />
<TodoFooter store={this.props.store} />
</div>
)
}
}
let store = new Store();
ReactDOM.render(<TodoList store={store} />, document.querySelector('#root'));
结尾
好啦,这期的前端丛林大冒险先到这里啦!这期我们深入了解了mobx的应用,写了一个计数器和TODOList。这期的内容比较不好理解,大家一定要多看几遍,好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!