React + Antd + Mobx改進以前簡單的todolist

以前用redux改進了todolist,用redux對應用狀態進行管理,今天換成mobx。項目地址css

1. 在create-react-app中使用裝飾器

  1. 使用react-app-rewired
yarn add customize-cra react-app-rewired @babel/plugin-proposal-decorators --save
複製代碼
  1. 項目根目錄新建config-overrides.js文件加入如下代碼
const { override, addDecoratorsLegacy } = require('customize-cra');
module.exports = override(
 addDecoratorsLegacy()
 );
複製代碼
  1. 修改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

能夠先學習官方文檔熟悉基本概念react

  1. 按照上面改造create-react-app建立的項目後,引入mobx
yarn add mobx mobx-react
複製代碼
  1. 修改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的狀態

  1. 修改以前的項目react-antd-todolist-simple,添加mobx相關包
yarn add mobx mobx-react
複製代碼
  1. 按照上面的步驟修改使其支持裝飾器
  2. 新建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()
複製代碼
  1. 新建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;
  }
}
複製代碼
  1. 改造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;
複製代碼
  1. 改造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.jsgit

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;
複製代碼
  1. 改造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;
複製代碼
  1. 將全部改過的組件都引入到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改造完畢。github

相關:React + Antd + Redux改進以前簡單的todolistjson

React + Antd + Redux改進以前簡單的todolistredux

相關文章
相關標籤/搜索