1:使用useState()实现一个todoList
把我们需要用到的数据结构都放在一个types.ts文件里面
//types.ts
export interface todoItemInterface {
id: number,
text: String,
completed: boolean
}
export interface todoListInterface {
todoList: todoItemInterface[]
}
实现TodoItem
//TodoItem.tsx
import * as React from "react";
import {todoItemInterface} from '../../types/types'
export const TodoItem: React.FC<todoItemInterface> = ({id, text, completed}) => (
<div style={{textDecoration: completed ? 'line-through' : 'none'}}>{text}</div>
);
实现TodoList
//TodoList.tsx
import * as React from "react";
import {todoListInterface} from '../../types/types'
import {TodoItem} from '../todoItem/TodoItem'
const TodoList: React.FC<todoListInterface> = ({todoList}) => (
<ul>
{todoList.map((todoItem) => (
<li key={todoItem.id}>
<TodoItem id={todoItem.id} text={todoItem.text} completed={todoItem.completed}/>
</li>
))}
</ul>
);
export default TodoList;
在App.tsx里面展示TodoList
//App.tsx
import * as React from 'react';
import {useState} from "react";
import TodoList from '../todoList/TodoList'
import {todoItemInterface} from '../../types/types'
function App() {
const [todos] = useState<todoItemInterface[]>([
{id: 0, text: 'clean house', completed: false},
{id: 1, text: 'cook dinner', completed: false}
]);
return (
<div>
<h6>React hooks example</h6>
<TodoList todoList={todos} />
</div>
);
}
export default App;
这样我们就能在页面上看到我们的todoList了:
Screen Shot 2019-12-09 at 7.10.08 PM.png
以上是用React hooks+Typescript实现todoList的最简单例子。但是我们的todoList现在还没有任何功能,甚至称不上是一个todoList。接下来,我们给我们的todoList加上toggle的功能。当我们点击一个未完成的todo,给这个todo在样式上加一个横线,表示这个已经完成。如果是点击已经完成的,则把横线去掉,表示并没有完成。
为了完成以上功能,我们有2个task要做:
1: 给todoListInterface添加toggle方法
2:实现toggle方法
3:把toggle方法绑定到每一个todo的onClick事件上
接下来就看看代码实现
1: 给todoListInterface添加toggle方法
//types.ts
export interface todoItemInterface {
id: number,
text: String,
completed: boolean
}
export interface todoListInterface {
todoList: todoItemInterface[],
toggleTodo: (id: number)=> void
}
2:实现toggle方法
//App.tsx
import * as React from 'react';
import {useState} from "react";
import TodoList from '../todoList/TodoList'
import {todoItemInterface} from '../../types/types'
function App() {
let [todos, setTodos] = useState<todoItemInterface[]>([
{id: 0, text: 'clean house', completed: false},
{id: 1, text: 'cook dinner', completed: false}
]);
let toggleTodo = (id: number): void => {
let index = todos.findIndex((item) => {
return item.id === id;
});
let newTodos = todos.map((todo, currentIndex) => {
if (currentIndex === index) {
return {...todo, completed: !todo.completed}
} else {
return todo;
}
});
//You have to call the setTodos() with a new array reference, Or the TodoItem will
// not render again.
setTodos(newTodos);
};
return (
<div>
<h6>React hooks example</h6>
<TodoList todoList={todos} toggleTodo={toggleTodo}/>
</div>
);
}
export default App;
3:把toggle方法绑定到每一个todo的onClick事件上
//TodoList.tsx
import * as React from "react";
import {todoListInterface} from '../../types/types'
import {TodoItem} from '../todoItem/TodoItem'
const TodoList: React.FC<todoListInterface> = ({todoList, toggleTodo}) => (
<ul>
{todoList.map((todoItem) => (
<li key={todoItem.id}
onClick={() => {toggleTodo(todoItem.id);console.log('hehe')}}
>
<TodoItem id={todoItem.id} text={todoItem.text} completed={todoItem.completed}/>
</li>
))}
</ul>
);
export default TodoList;
以上,就是实现toggle一个todo,给todo添加或者去掉横线的完整代码。
在toggle方法里面有一个很重要的点就是:
let toggleTodo = (id: number): void => {
//Other codes
let newTodos = todos.map( //Other codes);
setTodos(newTodos);
};
我们必须给setTodos( )方法传入一个新的array reference,这里我们使用了map( )方法,而map会返回一个新的array。如果依然是把todos传入给setTodos( ),即使complete属性改了,我们的todoItem也不会重新render,因为对于hooks来说,todos是一个array,它的reference没有变,那它就不会触发新的render。
比如,如果采用下面错误的写法,是不会触发新的render的:
let toggleTodo = (id: number): void => {
let index = todos.findIndex((item) => {
return item.id === id;
});
todos[index].completed = !todos[index].completed;
console.dir(todos);
setTodos(todos);
};
2: 安装mobx-react-lite, mobx
npm install mobx mobx-react-lite
使用mbox和react hooks的大概task,可以分为以下几步:
1:创建store
2:创建react context
3: 使得react的组件成为observer
4:通过useContext来获取store
接下来就来看一下怎么实现以上具体步骤
1:创建store,&& 2:创建react context
//TodoListStore.ts
import {createContext} from 'react'
import {decorate, observable, computed, action} from 'mobx'
import {todoItemInterface} from '../../types/types'
export class Todos {
todos: Array<todoItemInterface> = [
{id: 0, text: 'clean house', completed: false},
{id: 1, text: 'cook dinner', completed: false}
];
toggleTodo = (id: number): void => {
let index = this.todos.findIndex((item) => {
return item.id === id;
});
this.todos[index].completed = !this.todos[index].completed;
};
}
decorate(Todos, {
todos: observable
});
//创建react context
export default createContext(new Todos());
3: 通过useContext( )获取store
//App.tsx
import * as React from 'react';
import {useContext} from "react";
import TodoList from '../todoList/TodoList'
import todosContext from '../../stores/todoListStore/TodoListStore';
const App = () => {
const todosStore = useContext(todosContext);
return (
<div>
<h6>React hooks example</h6>
<TodoList todoList={todosStore.todos} toggleTodo={todosStore.toggleTodo}/>
</div>
);
};
export default App;
4: TodoList成为observer
//TodoList.tsx
import * as React from "react";
import {todoListInterface} from '../../types/types'
import {TodoItem} from '../todoItem/TodoItem'
import {observer} from 'mobx-react-lite'
const TodoList: React.FC<todoListInterface> = observer(function ({todoList, toggleTodo}) {
return (
<ul>
{todoList.map((todoItem) => (
<li key={todoItem.id}
onClick={() => {
toggleTodo(todoItem.id)
}}
>
<TodoItem id={todoItem.id} text={todoItem.text} completed={todoItem.completed}/>
</li>
))}
</ul>
)
});
export default TodoList;
因为我们的TodoList是真正使用了store的组件,所以它需要成为一个observer,当store里面的数据(这里是todosStore.todos)变化的时候,TodoList会重新render。
网友评论