美文网首页
实战:增删改查的不平凡

实战:增删改查的不平凡

作者: 这波能反杀 | 来源:发表于2022-04-21 21:38 被阅读0次

    学习了 useState 与 useRef ,结合前面几章内容,我们可以实现一个非常经典的功能:任务列表。

    当我点击新增时,编辑弹窗出现,输入框自动获得焦点

    输入任务描述之后,点击创建,此时任务创建成功,编辑弹窗消失,新增按钮再次出现

    结合 React 哲学 中的思想,我们可以很自然的想到该按钮中的数据有:

    // 任务列表
    const [jobs, setJobs] = useState<Array<Job>>([])
    
    // 编辑窗口显示与否
    const [show, setShow] = useState(false)
    
    // 每次输入时当前任务描述
    const [desc, setDesc] = useState('')
    

    单个任务的数据格式为

    interface Job {
      desc: string,
      id: string,
      createTime: number,
      isSelected: boolean
    }
    

    点击「新增按钮」,编辑弹窗出现,新增按钮消失,因此在 JSX 中,我们可以这样去表达他们的交互关系

    {show ? (
      <div className="dialog">
        <input
          onChange={event => setDesc(event.target.value)}
          ref={inputRef}
          placeholder="请输入任务描述"
          onBlur={() => { setTimeout(() => { setShow(false) }, 0) }}
        />
        <div className="create" onClick={add}>创建</div>
      </div>
    ) : <div className="add" onClick={() => setShow(true)}>新增</div>}
    

    input 框在失去焦点后编辑弹窗消失

    我们可以使用 useRef 获得 input 组件的引用,以实现弹窗出现之后 input 能自动获取焦点的效果

    const inputRef = useRef<HTMLInputElement>(null)
    

    但是需要注意的是,input 元素是在 show 变成 true ,并且组件真实 DOM 再次渲染完成之后才能获取引用,因此这里运用了一个之后会学习的知识来解决这个问题

    useEffect(() => {
    // show 变成 true,并且组件渲染完成之后执行
      if (show) {
        inputRef.current?.focus()
      }
    }, [show])
    

    当编辑弹窗出现,我们在弹窗中输入内容,需要将输入内容保存在 desc 字段中。

    基于只操作数据的思想,点击新增,其实只是往任务列表数据 jobs 中新增一个值

    function add() {
      jobs.push({
        id: randomId(),
        desc,
        isSelected: false,
        createTime: Date.now()
      })
      setJobs([...jobs])
      setDesc('')
    }
    

    这里需要注意为了让 React 感知到你的数组发生了变化,需要重新创建一个新的引用数组。否则 React 无法识别。

    jobs 表示任务列表,可以根据该数据遍历出整个列表

    {jobs.map((job, i) => (
      <div className="job-wrapper" key={job.id}>
        <div className="selected" onClick={() => troggleSelected(i)}>
          {job.isSelected && <div className="circle"></div>}
        </div>
        <div className="desc">{job.desc}</div>
        <div className="remove" onClick={() => remove(i)}>删除</div>
      </div>
    ))}
    

    删除与编辑都只需要修改对应的数组数据即可

    function remove(i: number) {
      jobs.splice(i, 1)
      setJobs([...jobs])
    }
    
    function troggleSelected(i: number) {
      jobs[i].isSelected = !jobs[i].isSelected
      setJobs([...jobs])
    }
    

    就这样,我们轻松实现了一个列表的增删改查,完整代码如下

    import { useEffect, useRef, useState } from 'react';
    import { randomId } from './utils/index'
    import logo from './logo.svg';
    import './App.css';
    
    interface Job {
      desc: string,
      id: string,
      createTime: number,
      isSelected: boolean
    }
    
    function App() {
      const [jobs, setJobs] = useState<Array<Job>>([])
      const [show, setShow] = useState(false)
      const [desc, setDesc] = useState('')
      const inputRef = useRef<HTMLInputElement>(null)
    
      useEffect(() => {
        if (show) {
          inputRef.current?.focus()
        }
      }, [show])
    
      function add() {
        jobs.push({
          id: randomId(),
          desc,
          isSelected: false,
          createTime: Date.now()
        })
        setJobs([...jobs])
        setDesc('')
      }
    
      function remove(i: number) {
        jobs.splice(i, 1)
        setJobs([...jobs])
      }
    
      function troggleSelected(i: number) {
        jobs[i].isSelected = !jobs[i].isSelected
        setJobs([...jobs])
      }
    
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <h1>React 知命境</h1>
          </header>
    
          <div className="container">
            <div className="task-header">
              <div className="title">进行中的任务</div>
              <div className="right">...</div>
            </div>
    
            {jobs.map((job, i) => (
              <div className="job-wrapper" key={job.id}>
                <div className="selected" onClick={() => troggleSelected(i)}>
                  {job.isSelected && <div className="circle"></div>}
                </div>
                <div className="desc">{job.desc}</div>
                <div className="remove" onClick={() => remove(i)}>删除</div>
              </div>
            ))}
    
            {show ? (
              <div className="dialog">
                <input
                  onChange={event => setDesc(event.target.value)}
                  ref={inputRef}
                  placeholder="请输入任务描述"
                  onBlur={() => { setTimeout(() => { setShow(false) }, 0) }}
                />
                <div className="create" onClick={add}>创建</div>
              </div>
            ) : <div className="add" onClick={() => setShow(true)}>新增</div>}
    
          </div>
        </div>
      );
    }
    
    export default App;
    

    很显然,代码功能虽然实现了,但是代码上并不简洁,学习了 React 哲学的同学可以思考一下,我们应该从哪些角度去思考如何进一步简化代码呢?下一章继续分享

    我是这波能反杀,关注我,成为 React 高手

    相关文章

      网友评论

          本文标题:实战:增删改查的不平凡

          本文链接:https://www.haomeiwen.com/subject/cedwertx.html