美文网首页
React进阶学习

React进阶学习

作者: xinmin | 来源:发表于2020-05-25 22:27 被阅读0次

    -工具类

    node、npm、eslint、pretter

    语法类

    es5、es6

    概念类

    SPA:单页面应用

    MPA:多页面应用

    PWA:渐进式 Web App

    PWA组成技术

    1. Service Worker:服务工作线程

      1.1 常驻内存运行

      1.2 代理网络请求

      1.3 依赖HTTPS

      // npm install serve -g 全局安装服务模块
      // 在项目目录下执行 serve 快速启动http本地服务器 localhost:5000
      // index.html
      <html>
          <head>
              <title>learn PWA</title>
          </head>
          <body>
              <script>
                  // 注册 service worker
                  navigator.serviceWorker.register('./sw.js', {
                      scope: '/'
                  }).then(registration => {
                      console.log(registration)
                  }, error => {
                      console.error(error)
                  })
              </script>
          </body>
      </html>
      
      // sw.js
      self.addEventListener('install', event => {
          console.log('install', event)
          // 在 install 执行后 5s 再激活 执行 activate
          /*
          event.waitUntil(new Promise(resolve => {
              setTimeout(resole, 5000)
          }))
          */
          // 强制停止旧的service worker 激活新的 service worker 
          event.waitUntil(self.skipWaiting())
      })
      
      self.addEventListener('activate', event => {
          console.log('activate', event)
          // 同样包含 event.waitUntil() 与 install 中用法一样
          event.waitUntil(self.clients.claim())    
      })
      
      self.addEventListener('fetch', event => {
          console.log('fetch', event)
      })
      
    1. Promise:”承诺”控制流

      2.1 优化回调地狱

      2.2 async/await 语法同步化

      2.3 Service Worker的API

      // callback回调方式
      readFile(filename, (err, content) => {
          parseXML(content, (err, xml) => {
              // 
          })
      })
      
      readFile(filename).then(content => parseXML(content)).then(xml => {}, err => {})
      // 推荐使用
      readFile(filename).then(content => parseXML(content)).then(xml => {}).catch(err => {})
      
      Promise.resolve(1)
      // 等价于
      new Promise(resolve => resolve(1))
      
      Promise.reject(error)
      // 等价于
      new Promise((resolve, reject) => reject(error))
      
      Promise.all()
      Promise.race()
      
      // 替代 Promise 需要 babel 转换
      async function readXML(filename) {
         const content = await readFile(filename) 
         const xml = await parseXML(content)
         return xml
      }
      
    2. fetch:网络请求

      3.1 比XMLHttpRequest更简洁

      3.2 Promise风格

      3.3 依旧存在不足

      <html>
          <head>
              <title>learn PWA</title>
          </head>
          <body>
              <script>
                  // 原始ajax请求
               const xhr = new XMLHttpRequset()
               xhr.responseType = 'json'
               xhr.onreadstatechange = () => {
                   if(xhr.readyState === 4){
                       if(xhr.status >= 200 && xhr.status < 300) {
                           console.log(xhr.response)
                            }
                        }
                    }
                    xhr.open('GET', './useinfo.json', true)
                    xhr.send(null)
                  
                    // fetch 实现
                      const req = new Request('./useinfo.json', {
                         method: 'GET',
                         headers: new Headers(),
                         credentials: 'include'    
                      })
                      /*
                      fetch('./useinfo.json', {
                    method: 'GET',
                         headers: new Headers(),
                         credentials: 'include' 
                      })
                      */
                       fetch(req)
                        .then(res => res.json())
                        .then(info => console.log(info))
                  
                    // 更好的方案 axios
              </script>
          </body>
      </html>
      
    3. cache API:支持资源的缓存系统

      4.1 缓存资源(css/scripts/image)

      4.2 依赖 Service Worker 代理网络请求

      4.3 支持离线程序运行

      // index.html
      <html>
          <head>
              <title>learn PWA</title>
          </head>
          <body>
              <h1>
                  hello PWA
              </h1>
              <script>
                  // 注册 service worker
                  navigator.serviceWorker.register('./sw.js', {
                      scope: '/'
                  }).then(registration => {
                      console.log(registration)
                  }, error => {
                      console.error(error)
                  })
              </script>
          </body>
      </html>
      
      // sw.js
      const CACHE_NAME = 'cache-v1'
      
      self.addEventListener('install', event => {
          console.log('install', event)
          event.waitUntil(caches.open(CACHE_NAME).then(cache => {
              cache.addAll([
                  './',
                  './index.css'
              ])
          }))  
      })
      
      self.addEventListener('activate', event => {
          console.log('activate', event)
           // 同样包含 event.waitUntil() 与 install 中用法一样
          event.waitUntil(caches.keys().then(cacheNames => {
              return Promise.all(cacheNames.map(cacheName => {
                  if(cacheName !== CACHE_NAME){
                      return caches.delete(cacheName)
                  }
              }))
          }))    
      })
      
      self.addEventListener('fetch', event => {
          console.log('fetch', event)
          event.respondwWidth(caches.open(CACHE_NAME).then(cache => {
              return cache.match(event.request).then(res => {
                  if(res) {
                      return res
                  }
                  return fetch(event.request).then(res => {
                      cache.put(event.request, res.clone())
                      return res
                  })
              })
          }))
      })
      
    4. Notification API:消息推送

      5.1 依赖用户授权

      5.2 适合在Service Worker中推送

      // 在页面上下文中查看用户授权
      Notification.permission // defalut 默认 granted 允许 denied 拒绝
      // 在页面上下文中弹出授权
      Notification.requestPermission().then(permission => console.log(permission))
      // 弹出通知消息
      new Notification("HELLO NOTIFICATION", {body: 'this is from console'})
      // 在 Service Worker 上下文 中不允许弹出授权请求
      // 在 Service Worker 上下文中 弹出通知消息
      self.registration.showNotification("HELLO NOTIFICATION", {body: 'this is from sw'})
      

      在webpack中开启PWA(create-react-app中已经集成service worker)

      谷歌推出 workbox
      

    效率类

    原则类

    eject

    npm run eject 打开create-react-appwebpack.config,js

    React 最新特性

    • context

      定义:提供一种方式,能够让数据在组件树中传递而不必一级一级手动传递

      import React, { createContext } from 'react'
      
      const BatteryContext = createContext()
      const OnlineContext = createContext()
      
      // 第三级组件
      class Leaf extend Component {
          render() {
              return (
                  <BatteryContext.Consumer>
                      {
                          battery => (
                            <OnlineContext.Consumer>
                                  {
                                      online => <h1>Battery: {battery}, Online: {String(online)}</h1>
                                  }
                              </OnlineContext.Consumer>
                          ) 
                      }
                  </BatteryContext.Consumer>
              )
          }
      }
      
      // 中间组件
      class Middle extend Component {
          render() {
              return <Leaf/>
          }
      }
      
      // 顶级组件
      class App extend Component {
          state = {
              battery: 60,
              online: false
          }
          render() {
              const { battery, online } = this.state
              return (
                <BatteryContext.Provider value={battery }>
                      <OnlineContext.Provider value={online}>
                          <button
                              type="button"
                              onclick={() => this.setState({battery: battery - 1})}   
                           >
                              点击-1
                          </button>
                           <button
                              type="button"
                              onClick={() => this.setState({online: !online})}   
                           >
                              点击切换
                          </button>
                      <Middle />
                      </OnlineContext.Provider>
                  </BatteryContext.Provider>
              )
          }
      }
      
    • ContextType简化context的使用

      import React, { createContext } from 'react'
      
      const BatteryContext = createContext(90) // 90 为默认值
      
      // 第三级组件
      class Leaf extends Component {
          static ContextType = BatteryContext
          render() {
              const battery = this.context
              return (
                 <h1>Battery: {battery}</h1>
              )
          }
      }
      
      // 中间组件
      class Middle extends Component {
          render() {
              return <Leaf/>
          }
      }
      
      // 顶级组件
      class App extends Component {
          state = {
              battery: 60
          }
          render() {
              const { battery, online } = this.state
              return (
                <BatteryContext.Provider value={battery }>              
                      <button
                          type="button"
                          onClick={() => this.setState({battery: this.battery - 1})}   
                       >
                          点击-1
                      </button>
                      <Middle />
                  </BatteryContext.Provider>
              )
          }
      }
      
    • lazy 与 Suspense:组件的懒加载

      // about 组件
      import React, { Component } from 'react'
      
      export default class About extends Component {
          render () {
              return (
                <h1>About</h1>
              )
          }
      }
      
      // APP 组件
      import React, { Component, lazy, Suspense } from 'react'
      
      const About = lazy(() => import(/* webpackChunkName: "about" */'./About.jsx'))
      
      class App extends Component {
          state = {
              hasError: false
          }
        // 捕捉异常的静态方法 
          static getDerivedStateFromError() {
              return { 
                  hasError: true 
              }
          }
          // 捕获异常的生命周期函数
          /*
          componentDidCatch () {
              this,setState({
                  hasErrir: true
              })
          }
          */
          render () {
              return (
                  if (this.state.hasError) {
                    return <div>error</div>
                }
                <div>
                      <Suspense fallback={<div>loading</div>}>
                        <About></About>
                      </Suspense>
                  </div>
              )
          }
      }
      
      export default App
      
    • memo 和 PureComponent:避免子组件的重复渲染

      import React, { Component, PureComponent} from 'react'
      
      /*
      class Foo extends Component {
          
          shouldComponentUpdate(nextProps, nextState) {
              if (nextProps.name === this.props.name) {
                  return false
              }
              return true
          }
        */
          
      class Foo extends PureComponent {
          
          /*
          shouldComponentUpdate(nextProps, nextState) {
              if (nextProps.name === this.props.name) {
                  return false
              }
              return true
          }
          */
          render() {
              console.log('Foo render')
              return null
          }
      }
      
      class App extends Component {
          state = {
              count: 0
          }
          render() {
            return (
                <div>
                      <button
                        type="button"
                          onClick={() => {this.setState({ count: this.count + 1 })}}
                      >Add</button>
                    <Foo name="xinmin" />
                  </div>
              )
          }
      }
      
      
      import React, { Component, PureComponent, memo } from 'react'
      
      const Foo = memo((props) => {
          console.log('Foo render')
          return <div>{props.person.age}</div>
      })
      
      class App extends Component {
          state = {
              count: 0,
              person: {
                  age: 1
              }
          }
          render() {
            return (
                <div>
                      <button
                        type="button"
                          onClick={() => {
                              person.age++
                              this.setState({ person })
                          }}
                      >Add</button>
                    <Foo person={person} />
                  </div>
              )
          }
      }
      

    Hooks

    • 使用useState简化状态的写法

      import React, { component } from 'react'
      
      class App extends Component {
          state = {
              count: 0
          }
          render() {
            const { count } = this.state
               return (
                <button
                    type="button"
                      onClick={() => {this.setState({count: count + 1})}}
                   >
                   点击 ({count})
                   </button>
               )
          }
      }
      
      export default App
      
      import React, { useState } from 'react'
      
      function App() {
          const [ count, setCount ] = useState(0)
          return (
              <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count})
              </button>
          )
      }
      
      export default App
      

      npm i eslint-plugin-react-hooks -D 可以检测Hooks的错误,需要在package.json中配置

      "eslintConfig": {
          "extends": "react-app",
          "plugins": [
              "react-hooks/rules-of-hooks": "error"
          ]
      }
      
      import React, { useState } from 'react'
      
      function App(props) {
          const [ count, setCount ] = useState(() => {
              console.log('initial count')
              return props.defaultCount || 0
          })
          return (
              <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count})
              </button>
          )
      }
      
      export default App
      
    • 使用useEffect简化副作用的写法

      import React, { component } from 'react'
      
      class App extends Component {
          state = {
              count: 0,
              size: {
                  width: document.documentElement.clientWidth,
                  height: document.documentElement.clientHeight
              }
          }
      
          onResize = () => {
              this.setState({
                  size: {
                      width: document.documentElement.clientWidth,
                      height: document.documentElement.clientHeight
                  }
              })
          }
        
          componentDidMount() {
            document.title = this.state.count
            window.addEventListenner('resize', this.onResize, false)
          }
      
          componentWillMount() {
            window.removeEventListener('resize', this.onResize, false)
          }
        
          componentDidUpdate() {
            document.title = this.state.count
          }
        
          render() {
            const { count, sizehis.state
               return (
                <button
                    type="button"
                      onClick={() => {this.setState({count: count + 1})}}
                   >
                   点击 ({count})
                   Size: {size.width}x{size.height}
                   </button>
               )
          }
      }
      
      export default App
      
      import React, { useState, useEffect } from 'react'
      
      function App(props) {
          const [ count, setCount ] = useState(0)
          const [ size, setSize ] = useState({
              width: document.documentElement.clientWidth,
              height: document.documentElement.clientHeight
          })
          
          const onResize = () => {
              setSize({
                  width: document.documentElement.clientWidth,
                height: document.documentElement.clientHeight
              })
          }
          
          useEffect(() => {
              document.title = count
          })
          
          useEffect(() => {
              window.addEventListener('resize', onResize, false)
              
              return () => {
                  window.removeEventListener('resize', onResize, false)
              }
          }, [])
          
          return (
              <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count})
                  Size: {size.width}x{size.height}
              </button>
          )
      }
      
      export default App
      
    • 使用useContext跨层级组件传值

      import React, { useState, createContext, useContext } from 'react'
      
      // 在类组件中使用context
      class Foo extends React.Component {
          render() {
              return (
                <CountContext.Consumer>
                      {
                          count => <h1>count: {count}</h1>
                      }
                  </CountContext.Consumer>
              )
          }
      }
      
      // 在类组件中使用 contextType 简化 context 的使用
      class Bar extends React.Component {
          static contextType = CountContext
          render() {
              const count = this.context
              return (
                <h1>{count}</h1>
              )
          }
      }
      
      // 在函数组件中使用 context
      function Counter() {
          const count = useContext(CountCountext)
          return (
            <h1>{count}</h1>
          )
      }
      
      const CountContext = createContext()
      
      function App(props) {
          const [ count, setCount ] = useState(0)
          return (
              <div>
                  <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count})
                </button>
                  <CountContext.Provider value={count}>
                      <Foo/>
                      <Bar/>
                      <Counter/>
                  </CountContext.Provider>
              </div>
          )
      }
      
      export default App
      
    • 使用useMemo优化性能,避免子组件重复渲染

      import React, { useState, useMemo, memo, useCallback } from 'react'
      
      const Counter = memo(function Counter(props) {
          console.log('Counter render!')
          return (
            <h1 onClick={props.onClick}>count:{props.count}</h1>
          )
      })
      
      function App(props) {
          const [ count, setCount ] = useState(0)
          
          const double = useMemo(() => {
              return count*2
          }, [count===3])
          
          const onClick = useCallback(() => {
              console.log('Click')
          }, [])
          
          // 如果 useMemo 返回的是一个函数 简写成 useCallback
          /*
          const onClick = useMemo(() => {
              return () => {
                  console.log('Click')
              }
          }, [])
          */
          return (
              <div>
                  <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count}), double: {double}
                </button>
                <Counter count={count} onClick={onClick}/>
              </div>
          )
      }
      
      export default App
      
    • 使用useRef

      import React, { PureComponent, useState, useMemo, memo, useCallback, useRef } from 'react'
      
      /* 函数组件不能别 ref 获取
      const Counter = memo(function Counter(props) {
          console.log('Counter render!')
          return (
            <h1 onClick={props.onClick}>count:{props.count}</h1>
          )
      })
      */
      class Countter extends PureComponent {
          speak() {
              console.log(`now counter is:${this.props.count}`)
          }
          render() {
              const { props } = this
              return (
                <h1 onClick={props.onClick}>count:{props.count}</h1>
              )
          }
      }
      
      function App(props) {
          const [ count, setCount ] = useState(0)
          const counterRef = useRef()
          
          const double = useMemo(() => {
              return count*2
          }, [count===3])
          
          const onClick = useCallback(() => {
              console.log('Click')
              //console.log(counterRef.current)
              counterRef.current.speak()
          }, [counterRef])
          
          return (
              <div>
                  <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count}), double: {double}
                </button>
                <Counter ref={counterRef} count={count} onClick={onClick}/>
              </div>
          )
      }
      
      export default App
      
      import React, { PureComponent, useEffect, useState, useMemo, memo, useCallback, useRef } from 'react'
      
      /* 函数组件不能别 ref 获取
      const Counter = memo(function Counter(props) {
          console.log('Counter render!')
          return (
            <h1 onClick={props.onClick}>count:{props.count}</h1>
          )
      })
      */
      class Countter extends PureComponent {
          speak() {
              console.log(`now counter is:${this.props.count}`)
          }
          render() {
              const { props } = this
              return (
                <h1 onClick={props.onClick}>count:{props.count}</h1>
              )
          }
      }
      
      function App(props) {
          const [ count, setCount ] = useState(0)
          const counterRef = useRef()
          const it = useRef()
          
          const double = useMemo(() => {
              return count*2
          }, [count===3])
          
          const onClick = useCallback(() => {
              console.log('Click')
              //console.log(counterRef.current)
              counterRef.current.speak()
          }, [counterRef])
          
          useEffect(() => {
              it.current = setInterval(() => {
                  setCount(count => count + 1)
              }, 1000)
          }, [])
          
          useEffect(() => {
              if(count >= 10){
                  clearInterval(it.current)
              } 
          },[])
          
          return (
              <div>
                  <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count}), double: {double}
                </button>
                <Counter ref={counterRef} count={count} onClick={onClick}/>
              </div>
          )
      }
      
      export default App
      
    • 自定义的Hooks

      import React, { PureComponent, useEffect, useState, useMemo, memo, useCallback, useRef } from 'react'
      
      // 自定义hook函数
      function useCount(defaultCount) {
          const [count, setCount] = useState(defaultCount)
          const it = useRef()
      
          useEffect(() => {
              it.current = setInterval(() => {
                  setCount(count => count + 1)
              }, 1000)
          }, [])
          
          useEffect(() => {
              if(count >= 10){
                  clearInterval(it.current)
              } 
          })
          return [count, setCount]
      }
      
      // 自定义hook函数,返回 jsx
      function useCounter(count) {
          return (
            <h1>{count}</h1>
          )
      }
      
      function App(props) {
          const [ count, setCount ] = useCount(0)
          const Counter = useCounter(count)
          
          return (
              <div>
                  <button
                  type="button"
                  onClick={() => {setCount( count + 1)}}
                  >
                  点击 ({count})
                </button>
                { Counter }
              </div>
          )
      }
      
      export default App
      

    Hooks的使用规则

    1. 只在最顶层中使用hooks,不在循环、判断等条件下使用
    2. 只在函数组件中使用,不在普通函数中使用
    

    Hooks常见问题

    对传统React编程影响

    1. 生命周期


      图片.png
      1. constructor:在函数组件中用useState来初始化

      2. getDerivedStateFromProps

        class Counter extends Component {
            state = {
                overflow: false
            }
            static getDerivedStateFromProps(props, state) {
                if(props.count > 10) {
                    return {
                        overflow: true
                    }
                }
            }
        }
        
        function Counter(props) {
            const [overFlow, setOverflow] = useState(false)
            
            if(props.count > 10) {
                setOverflow(true)
            }
        }
        
      3. shouldComponentUpdate:在函数组件中 使用memo

      4. render:函数组件本身返回 jsx

      5. componentDidMount

      6. componentWillUnmount

      7. componentDidUpdate

        function App() {
            useEffect(() => {
                // componentDidMount
                return () => {
                    // componentWillUnmount
                }
            }, [])
            
            let renderCounter = useRef(0)
            renderCounter.current++
            
            useEffect(() => {
                if(renderCounter > 1){
                    // componentDidUpdate
                }
            })
        }
        
    1. 类成员变量如何映射到Hooks?
    class App {
        it = 0
    }
    
    funtion App() {
        const it = useRef(0)
    }
    
    1. Hooks中如何获取历史props和state?
    function Counter() {
        const [count, setCount] = useState(0)
        const prevCountRef = useRef()
        
        useEffect(() => {
            prevCountRef.current = count
        })
        
        const prevCount = prevCountRef.current
        
        return <h1>Now: {count}, before: {prevCount}</h1>
    }
    
    1. 如何强制更新一个hooks组件?
    function Counter() {
        const [count, setCount] = useState(0)
        const [updater, setUpdater] = useState(0)
        const prevCountRef = useRef()
    
        function forceUpdater() {
            setUpdater(updater => updater + 1)
        }
    
        useEffect(() => {
            prevCountRef.current = count
        })
    
        const prevCount = prevCountRef.current
    
        return <h1>Now: {count}, before: {prevCount}</h1>
    }
    

    Redux

    1. 状态容器与数据流管理

    2. redux的三大原则

       2.1 单一数据源
      
       2.2 状态不可变
      
       2.3 纯函数修改状态 
      
    3. 没有redux的世界,纯 Hooks 开发 TodoList

      .todo-list {
          width: 550px;
          margin: 300px auto;
          background: #fff;
          box-shadow: 0 2px 4px 0 rgba(0,0,0,0.2), 0 25px 50px 0 rgba(0,0,0,0.1)
      }
      .control h1 {
          width: 100%;
          font-size: 100px;
          text-align: center;
          margin: 0;
          color: rgba(175,47,47,0.15);
      }
      .control .new-todo {
       padding: 16px 16px 16px 60px;
          border: 0;
          outline: none;
          font-size: 24px;
          box-sizing: border-box;
          line-height: 1.4rem;
          box-shadow: inset 0 -2px 1px rgba(0,0,0,0.3);
      }
      .todos {
          margin: 0;
          padding: 0;
          list-style: none;
      }
      .todo-item {
          margin: 0;
          padding: 0;
          list-style: none;
          font-size: 24px;
          display: flex;
          align-item: center;
      }
      .todo-item input {
          display: block;
          width: 20px;
          height: 20px;
          margin: 0 20px;
      }
      .todo-item label {
          flex: 1;
          padding: 0;
          line-height: 1.2rem;
          display: block;
      }
      .line-item label.complete {
          text-decoration: line-through;
      }
      .todo-item button {
          border: 0;
          outline: 0;
          display: block;
          width: 40px;
          text-align: center;
          font-size: 30px;
          color: #cc9a9a;
      }
      
      import React, { useState, useCallback, useRef, memo } from 'react'
      import './App.css'
      
      let idSeq = Date.now()
      const LS_KEY = '_$-todos_'
      
      const Control = memo(function Control(props) {
          const { addTodo } = props
          const inputRef = useRef()
          
          const onSubmit = (e) => {
              e.preventDefault()
              const newText = inputRef.current.value.trim()
              if(newText.length === 0) {
                  return
              }
              addTodo({
                  id: ++idSeq,
                  value: newText,
                  complete: false
              })
              inputRef.current.value = ''
          }
          
          return (
           <div className="control">
               <h1>
                   todos
                  </h1>
                  <form onSubmit={}>
                   <input 
                           type="text"
                          ref={inputRef}
                          className="new-todo"
                          placeholder="输入新的待办"
                       />
                  </form>
              </div>
          )
      })
      
      const TodoItem = memo(function TodoItem(props) {
          const {
              todo: {
                  id,
                  text,
                  complete
              },
              toggleTodo,
              removeTodo
          } = props
          
          const onChange = () => {
              toggleTodo(id)
          }
          
          const onRemove = () => {
              removeTodo(id)
          }
          
          return (
           <li className="todo-item">
               <input type="checkbox" onChange={onChange} checked={complete}/>
                  <label className={complete ? 'complete' : ''}>{text}</label>
                  <button onClick={onRemove}>$#xd7</button>
              </li>
          )
      })
      
      const Todos = memo(funtion Todos(props) {
          const { todos, toggleTodo, removeTodo } = props
          return (
           <ul>
                  {
                      todos.map(todo => {
                          return (<todoItem 
                                       key={todo.id}
                                       todo={todo}
                                       toggleTodo={toggleTodo}
                                      removeTodo={removeTodo}
                                 />)
                      })
                  }
              </ul>
          )
      })
      
      function TodoList() {
          const [todos, setTodos] = useState([])
          
          const addTodo = useCallback((todo) => {
              setTodos(todos => [...todos, todo])
          },[])
          
          const removeTodo = useCallback((id) => {
              setTodos( todos => todos.filter(todo => {
                  return todo.id !== id
              }))
          }, [])
          
          const toggleTodo = useCallback((id) => {
              setTodos(todos => todos.map(todo => {
                  return todo.id === id
                      ? {
                       ...todo,
                       complete: !todo.complete
                      }
                   : todo
              }))
          }, [])
          
          // 读取的副作用必须在写入之前才不会永远是空数组
          useEffect(() => {
             const todos = JSON.parse(localStorage.getItem(LS_KEY) || '[]')
             setTodos(todos)
          },[])
          
          // 写入
          useEffect(() => {
              localStorage.setItem(LS_KEY, JSON.stringify(todos))
          }, [todos])
           
          return(
           <div className="todo-list">
                  <Control addTodo={addTodo}/>
                  <Todos removeTodo={removeTodo} toggleTodo={toggleTodo} todos={todos}/>
           </div>
          )
      }
      
      export default TodoList
      

    相关文章

      网友评论

          本文标题:React进阶学习

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