使用 React Context, Hooks, Immer, Golang 创建一个TodoList

框架

浏览数:74

2019-9-7

背景

上周末在家没啥事, 想写点东西,一时不知道写什么好。 然后就想起来一个去了Airbnb 的同学, 说Airbnb 的面试风格是以实操为主,她面了8轮, 轮轮都是写代码(2018年), 令人十分害怕。

代码题目都不是很难, 简单分享几道题:

然后我就看到了实现一个Todo List

这种题目不难, 但是灵活度很高, 没有什么限制条件, 你随意发挥, 可以从多方面考察候选人。

就拿这道题下手吧, 开造。

[ 文末有本文全部的源代码, 通过这个例子, 你会了解 React ContextHooks 的使用方式, 以及 如何使用 Golang 编写 CURD Api]

废话不多说了,进入正题。

功能实现

首先就是新建一个项目, 不限工具, CRA 什么的都可以, 我就用了之前自己写的一个工具, 简单起了项目, 不过这些都无所谓了, 直接上代码:

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(<App />, document.getElementById('root'));

App

// App.js

import React from "react";
import { TodosProvider } from './Store'
import { TodoList } from './TodoList'

const App = () => {
  return (
    <TodosProvider>
      <TodoList />
    </TodosProvider>
  );
}

export default App

Store

这里我创建了两个Context, 一个用于存储数据, 另一个用于更新数据。

例如:

const initialState = []

const StateContext = React.createContext(initialState)
const UpdateContext = React.createContext(null)

被 Context 包裹的组件可以取用这些传入的值。

完整代码:

// Store.js
import React from 'react'
import produce from 'immer'

// An array of todos, where a todo looks like this: 
// { id: string; title: string; isCompleted: boolean }
const initialTodos = [{
  id: 1,
  title: "One",
  isCompleted: false
}, {
  id: 2,
  title: "Two",
  isCompleted: false
}, {
  id: 3,
  title: "Three",
  isCompleted: false
}]

const StateContext = React.createContext(initialTodos)
const UpdateContext = React.createContext(null)

export function TodosProvider({ children }) {
  const [todos, updateTodos] = React.useReducer(produce, initialTodos)
  return (
    <UpdateContext.Provider value={updateTodos}>
      <StateContext.Provider value={todos}>
        {children}
      </StateContext.Provider>
    </UpdateContext.Provider>
  )
}

export function useTodos() {
  return [React.useContext(StateContext), React.useContext(UpdateContext)]
}

Todo主体

这里的实现也很简单,我先简单做了两个小功能, 一个是完成某一项, 一个是删除某一项, 完整代码:

// TodoList
import React from 'react';
import { useTodos } from './Store'

export function TodoList() {
  const [todos, updateTodos] = useTodos()

  const deleteTodo = id => updateTodos(todos => {
    const todoIdxToDelete = todos.findIndex(todo => todo.id === id)
    todos.splice(todoIdxToDelete, 1)
  })

  const completeTodo = id => updateTodos(todos => {
    todos.find(todo => todo.id === id).isCompleted = true
  })

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id} style={todo.isCompleted ? { color: 'red' } : { color: 'green' }}>
          <span>{todo.title}</span>
          <button onClick={() => deleteTodo(todo.id)}>Delete</button>
          <button onClick={() => completeTodo(todo.id)}>Complete</button>
        </li>
      ))}
    </ul>
  )
}

DEMO:

下面把新增也一起做了。

想着都已经做到这一步了, 数据一刷新就没了, 干脆存到数据库里吧, 最终的样子:

后端接口实现:

看下这几个CURD接口是怎么写的:

Api: C

Api: U

Api: R

Api: D

代码都比较简单, 一眼就能看明白是做什么的, 就不多做解释了。

后端完整的代码在这里:

https://github.com/beMySun/go…

前端部分完整的代码在这里:
https://github.com/beMySun/re…

数据库用的 mySql, 建库, 加改表可以用这个Sequel Pro, 很方便

如果不想用这个, 也可以用命令行工具来操作。

再贴几个会用的命令吧:

show databases;

use database;

describe todos_tab;

select * from todos_tab limit 10;

加一个字段:text

加一个字段:isCompleted

最后

功能很简单, 但是做的时候需要考虑的东西还是很多的。

如果有朋友也想学习下, 可以把代码下载下来自己运行跑一下。

后端完整的代码在这里:
https://github.com/beMySun/go…

前端部分完整的代码在这里:
https://github.com/beMySun/re…

这个后端的demo 包含了完整的CURD, 是一个很好的学习demo, 希望对大家的学习有所帮助。

才疏学浅, 行文难免有纰漏。

若有错误, 欢迎指正,谢谢。

以上。

如果本文对你有帮助, 别忘了点赞呦~

参考资料:
https://learnku.com/golang/t/…
https://gorm.io/docs/update.h…

作者:皮小蛋