【前端丛林】React这样服用,效果更佳(13)

105 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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。这期的内容比较不好理解,大家一定要多看几遍,好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!