美文网首页
用 1 个Todolist 学会常用原生 Hook

用 1 个Todolist 学会常用原生 Hook

作者: 喜悦的狮子 | 来源:发表于2020-08-29 00:35 被阅读0次
    Hook 第 2 篇

    序言:

    看完学会使用 useCallback, useState, useRef, useEffect, useContext, useMemo, useImperativeHandle 7个常用 React 原生 Hook。

    Github:源码地址

    1、Context

    Context 让我们能够在组件树中,不运用props 就可以进行数据传递。
    通过使用context,我们可以避免通过中间元素传递props
    在过往我们必须使用 Class.Provider, Class.consumer……api完成数据的传递,
    但是有了useContext之后,大大简化了代码。我们用 Class.Provider 把子组件包裹并 拿到被传递的数据后,
    仅仅需要使用 useContext(Context对象)就可以拿到传递的数据。

    import React, { useCallback, forwardRef, useState, useRef, useEffect, createContext, useContext, memo, useMemo, useImperativeHandle } from 'react';
    import './App.css'
    let idSeq = Date.now(); // 作为key
    // 创建一个Context对象
    const CountContext = createContext()
    

    2、 memo

    memo可以优化子组件,当传入的数据不发生改变时不触发重渲染,
    要注意的是如果实现中拥有 useState 或 useContext 当 context 发生变化时仍然会触发重渲染

    const Control = memo((props) => {
      // 父组件传递的添加数据的方法
      const { addTodo } = props;
      // 使用 useContext 拿到了 Context组件传递的数据
        const name = useContext(CountContext)
        const inputRef = useRef(); 
        const onSubmit = (e) => {
          e.preventDefault();
          const newText = inputRef.current.value.trim(); // trim() 清除value的空白
          if (newText.length === 0) {
            return;
          } 
          addTodo({ 
            id: ++idSeq,
            text: newText,
            complete: false,
          });
          // 提交后清空
          inputRef.current.value = ''
        }
    
    

    3、useRef

    有两个使用场景,一个是拿到DOM节点 或组件实例,一个是返回一个在整个生命周期都保持不变的ref对象。

    return (
         <div className="control">
           <h1>TODOS</h1> 
           <span>{name}</span>
           <form action="" onSubmit={onSubmit}>
             <input type="text" className='new-todo' placeholder="请输入内容" ref={inputRef}/>
           </form>
         </div>
       )
     }
    ) 
    

    5、useImperativeHandle

    这里看我前一篇 Hooks 的 useImperativeHandle 怎么用?
    https://www.jianshu.com/p/bf9f66ac3f9c

    const TestRef = forwardRef((props, ref) => {
      useImperativeHandle(ref, () => ({
        result: "传输成功"
      }))
    })
    
    // 列表
    const Todos = memo(
      function Todos(props) {
        const ref = useRef();
        const { todos, toggleTodo, removeTodo } = props;
        
        useEffect(() => {
          console.log(ref.current, "传输成功")
        }, [])
        return (
          <>
          <ul>
            { 
              todos.map( todo => {
                return (<TodoItem 
                  key={todo.id}
                  todo={todo}
                  toggleTodo={toggleTodo}
                  removeTodo={removeTodo}
                />)
              })
            }
          </ul>
          <TestRef ref={ref}/>
          </>
        )
      }
    )
      
    // 列表项
    const TodoItem = forwardRef((props, ref) => {
      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': null}>{text}</label>
          <span onClick={onRemove}>x</span>
        </li>
      )
    })  
    
    const STON_KEY = '_$-todos_';
    

    6、 useState

    在下面的代码中,我们声明了一个叫 count 的 state 变量,然后把它的初始值设置为 0。
    React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。我们可以通过调用 setCount 来更新当前的 count。

    function TodoList () {
      const [todos, setTodos] = useState([]); // 记录初始化
      const [name] = useState("石小阳") // 名称初始化
      const [count, setCount] = useState(0) // 点赞数初始化
      // 新建记录
      const addTodo = (todo) => {
        setTodos(() => [...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
          }))
        }, [])
    

    7、 useEffect

    useEffect 叫做副作用机制,它可以代替钩子函数在特定的生命周期执行特定行为。
    在组件每次渲染之后调用,并且可以根据自定义的状态决定调用或者不调用。

        useEffect(() => {
          // 读取记录
          const todos = JSON.parse(localStorage.getItem(STON_KEY))
          setTodos(todos);
        }, [])
    
        useEffect(() => {
          // 每次重新渲染都把数据保存
          localStorage.setItem(STON_KEY,  JSON.stringify(todos));  
        }, [todos]);
    
    

    8、 useMemo 与 useCallback

    使用下面的写法,onClick 每次传递到组件中都会发生一次渲染

       // const onClick = () => {
       //   console.log('Click')
       // }
    
       // const onClick = useMemo(() => {
       //   return  () => {
       //     console.log('Click')
       //   }
       // }, [])
       // 如果返回的函数可以使用 useCallback 省略顶层函数
    
       // useMemo(() => fn)
       const onClick = useCallback(() => {
         console.log('click')
       }, [])
       // 如果产生的函数不变的话就会直接抛弃
    
       return(
       <div className="todo-list">
         <CountContext.Provider value={name}>
           <button type="button" onClick={() => {setCount(count + 1)}}>点赞</button> {count}
           <Control addTodo={addTodo} />
         </CountContext.Provider>
         {/* 每次渲染onClick 的句柄发生变化 */}
         <Todos todos={todos} onClick={onClick} removeTodo={removeTodo} toggleTodo={toggleTodo}/>
       </div>
     )
    }
    
    export default TodoList;
    
    

    相关文章

      网友评论

          本文标题:用 1 个Todolist 学会常用原生 Hook

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