之前用redux改进了todolist,用redux对应用状态进行管理,今天换成mobx。项目地址
1. 在create-react-app中使用装饰器
- 使用react-app-rewired
yarn add customize-cra react-app-rewired @babel/plugin-proposal-decorators --save
复制代码
- 项目根目录新建config-overrides.js文件加入以下代码
const { override, addDecoratorsLegacy } = require('customize-cra');
module.exports = override(
addDecoratorsLegacy()
);
复制代码
- 修改package.json文件如下
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
复制代码
2. 了解mobx
可以先学习官方文档熟悉基本概念
- 按照上面改造create-react-app创建的项目后,引入mobx
yarn add mobx mobx-react
复制代码
- 修改APP.js实现一个简单的domo
- @observable 可以在实例字段和属性 getter 上使用, 对于对象的哪部分需要成为可观察的
- @action 用来修改observables状态
- @computed可以根据现有的状态或其它计算值衍生出的值,可以在任意类属性的 getter 上使用 @computed 装饰器来声明式的创建计算属性
- @observer mobx与react结合的桥梁
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// import App from './App';
import * as serviceWorker from './serviceWorker';
import {observable, action, computed} from 'mobx'
import {observer} from 'mobx-react'
class Store {
@observable timer = 0
@computed get doubleTimer () {
return this.timer * 2
}
@action.bound add () {
this.timer ++
}
}
@observer
class App extends React.Component {
render () {
const {doubleTimer, add} = this.props.store
return (
<div>
<span>{doubleTimer}</span>
<button onClick={() => add()}>+</button>
</div>
)
}
}
ReactDOM.render(<App store={new Store()}/>, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
复制代码
3. 使用mobx管理todolist的状态
- 修改之前的项目react-antd-todolist-simple,添加mobx相关包
yarn add mobx mobx-react
复制代码
- 按照上面的步骤修改使其支持装饰器
- 新建store/index.js
import {observable, action} from 'mobx'
import list from '../data'
class Store {
@observable todos = list
@action addTodo = (todo) => { // 增
this.todos = [...this.todos, todo]
}
@action deleteTodo = (id) => { // 删
this.todos = this.todos.filter(item => {
return item.id !== id
})
}
@action completeTodo = (id) => { // 改
this.todos = this.todos.map(item => {
let obj = item.id === id ? {...item, isComplete: !item.isComplete} : item
return obj
})
}
@action searchTodo = (content) => { // 查
if (content === '') {
this.todos = list
} else {
this.todos = this.todos.filter(item => {
return item.content.indexOf(content) !== -1
})
}
}
}
export default new Store()
复制代码
- 新建Provider组件作用和react-redux里面的Provider类似,父子组件之间通过context将store传递下去
import React, {Component} from 'react';
import PropTypes from 'prop-types'
export default class Provider extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
};
static childContextTypes = {
store: PropTypes.object,
};
getChildContext() {
return {
store: this.props.store
}
}
render() {
return this.props.children;
}
}
复制代码
- 改造components/TodoItem.js
import React from 'react';
import { Typography, Button } from 'antd';
import PropTypes from 'prop-types'
import {observer} from 'mobx-react'
import './TodoItem.less'
const { Text } = Typography;
@observer
class TodoItem extends React.Component {
static contextTypes = {
store: PropTypes.object,
};
render () {
let { content, isComplete, id } = this.props
let { completeTodo, deleteTodo } = this.context.store
return (
<div className="item-container" onDoubleClick={() => completeTodo(id)} style={{cursor: 'pointer'}}>
<Text delete={isComplete}>{content}</Text>
<Button type="primary" icon="delete" onClick={() => deleteTodo(id)}></Button>
</div>
)
}
}
export default TodoItem;
复制代码
- 改造components/DataList.js
import React from 'react';
import {observer} from 'mobx-react'
import TodoItem from './TodoItem'
import PropTypes from 'prop-types'
import { List } from 'antd';
@observer
class DataList extends React.Component {
static contextTypes = {
store: PropTypes.object,
};
render () {
const {todos} = this.context.store
return (
<List
bordered
dataSource={todos}
renderItem={item => (
<List.Item>
<TodoItem {...item}/>
</List.Item>
)}
/>
)
}
}
export default DataList;
复制代码
7.改造components/Form.js
import React from 'react';
import {observer} from 'mobx-react'
import PropTypes from 'prop-types'
import { Input } from 'antd';
const Search = Input.Search;
@observer
class Form extends React.Component {
static contextTypes = {
store: PropTypes.object,
};
render () {
const {searchTodo} = this.context.store
return (
<Search
placeholder="请输入搜索内容"
style={{marginBottom: '8px'}}
onSearch={value => searchTodo(value)}
enterButton
/>
)
}
}
export default Form;
复制代码
- 改造componets/Footer.js
import React, { Component } from 'react';
import {observer} from 'mobx-react'
import { Button, Input } from 'antd';
import PropTypes from 'prop-types'
@observer
class Footer extends Component {
static contextTypes = {
store: PropTypes.object,
};
constructor(props) {
super(props)
this.state = {
isAdd: false,
addTodo: {}
}
}
handleClick () {
this.setState({
isAdd: true
})
}
handleChange (e) {
this.setState({
addTodo: {
content: e.target.value,
id: new Date(),
completed: false
}
})
}
handleConfirm () {
this.context.store.addTodo(this.state.addTodo)
this.setState({
isAdd: false
})
}
render () {
let addBtn = <Button type="primary" onClick={this.handleClick.bind(this)}>新增</Button>
let addComponent = <div style={{display: 'flex'}}>
<Input onChange={e => this.handleChange(e)} style={{marginRight: '10px'}}/>
<Button type="primary" onClick={this.handleConfirm.bind(this)}>确认</Button>
</div>
let component = this.state.isAdd ? addComponent : addBtn
return (
<div style={{marginTop: '10px'}}>
{component}
</div>
)
}
}
export default Footer;
复制代码
- 将所有改过的组件都引入到todoList中
import React from 'react'
import {observer} from 'mobx-react'
import { Layout } from 'antd';
import Form from './components/Form'
import DataList from './components/DataList'
import Footer from './components/Footer'
import './todolist.less'
import store from './store'
import Provider from './components/Provider'
const { Header, Content} = Layout;
const TodoListApp = observer(() => {
return (
<Provider store={store}>
<Layout className="todolist-layout">
<Header>
<h3 className="logo">TodoList</h3>
</Header>
<Content className="todolist-content">
<Form />
<DataList />
<Footer />
</Content>
</Layout>
</Provider>
)
})
export default TodoListApp
复制代码
重新运行yarn start,至此使用mobx对todolist改造完毕。