Skip to content

React Hook的一些实验性质的代码。

html
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/immer@5.2.1/dist/immer.umd.min.js"></script>
<script type="text/babel">

  const $ = {
    motto: 'Forever youthful. Forever weeping.',
    todos: [
      {
        uid: '377368e9f4384',
        text: '读完《百年孤独》',
        status: true
      },
      {
        uid: 'b7a8656148c44',
        text: '学会周杰伦的《蒲公英的约定》',
        status: false
      }
    ],
    yesIcon: ' √ ',
    delIcon: ' × ',
    addIcon: ' + ',
    tip: 'You did nothing...(@_@;) ',
    copyRight: `Copyright © ${new Date().getFullYear()} Lelie Chiang. All rights reserved `
  }

  const genUid = () => Math.random().toString(16).substring(2)

  const WishTodo = ({ addTodo }) => {
    const [input, setInput] = React.useState('')
    const handleInput = () => !!input && addTodo(input) && setInput('')
    const handleEnter = (e) => e.keyCode === 13 && handleInput()
    return (
      <div className='hero'>
        <input
          type='text'
          value={input}
          onChange={e => setInput(e.target.value)}
          onKeyDown={handleEnter}
        />
        <span className='action add' onClick={handleInput}>{$.addIcon}</span>
      </div>
    )
  }

  const App = () => {
    const [todos, setTodos] = React.useState($.todos)

    const TodoItem = ({ todo, index }) => {
      return (
        <li>
          <span className={todo.status ? 'done' : ''}>{todo.text}</span>
          <span className='action del' onClick={() => delTodo(index)}>{$.delIcon}</span>
          <span className='action yes' onClick={() => yesTodo(index)}>{$.yesIcon}</span>
        </li>
      )
    }

    const yesTodo = (index) => {
      const { status } = todos[index]
      !status ? setTodos(immer.produce(todos => { todos[index].status = !status })) : null
    }

    const delTodo = (index) => {
      setTodos(immer.produce(todos => { todos.splice(index, 1) }))
    }

    const addTodo = (input) => {
      const exist = todos.some(it => it.text.trim() === input.trim())
      if (!exist) {
        setTodos(immer.produce(todos => { todos.push({ text: input, status: false, uid: genUid() }) }))
        return true
      }
      return false
    }

    return (
      <div className='_app'>
        <div className='container'>
          <div className='motto'><span>{$.motto}</span></div>
          <div className='todos'>
            {
              todos.length ? <ul>
                {todos.map((it, idx) => <TodoItem key={it.uid} todo={it} index={idx} />)}
              </ul> : <span className='tip'>{$.tip}</span>
            }
          </div>
          <div className='wish'>
            <WishTodo addTodo={addTodo} />
          </div>
        </div>
        <div className='footer'>{$.copyRight}</div>
      </div>
    )
  }

  ReactDOM.render(
    <App />,
    document.getElementById('root')
  )
</script>
  • [useEffect]:React 会在执行当前effect之前对上一个effect进行清除。思考一下分别注释didCancel = trueclearTimeout(timer),以及放开它们会出现怎样的效果?console.error('didCandel')会执行几次?
jsx
const App = () => {
    const [show, setShow] = useState('')
    const [input, setInput] = useState('')
    useEffect(()=> {
      let didCancel = false
      let timer = null
      const delay = async () => {
        const data = await new Promise((resolve, reject) => {
        timer = setTimeout(() => {
          resolve(input)
        }, 1000)
        })
        if (!didCancel) {
        console.error(data)
        setShow(input)
        }
      }
      delay()
      return () => {
        console.error('didCancel')
        // didCancel = true
        // clearTimeout(timer)
      }
    }, [input])
    
    return (
      <div>
        <p>{ show }</p >
        <input  onChange={(e) => setInput(e.target.value) } value={input} />
      </div>
    )
  }
jsx
  const app = (props) => {
    const isInitRef = useRef(false)
    const domRef = useRef(null)
    useEffect(()=> {
        const dom = domRef.current
        if (isInitRef.current) {
          // do something
        } else {
          isInitRef.current = true
        }
    }, [props.data])

    return (
      <div ref={domRef}></div>
    )
  }
js
<script type="text/babel">

  const App = (props) => {
    const {
      listData,
      itemHeight = 30,
      itemCount = 10,
      emptyTip = "暂无数据..."
    } = props

    let [list, setList] = React.useState(listData)
    const [offset, setOffset] = React.useState(0)
    const [scroll, setScroll] = React.useState(0)

    const items = React.useMemo(() => {
      return list.length ? list.slice(offset, offset + itemCount) : [emptyTip]
    }, [offset, list])

    const containerHeight = React.useMemo(() => {
      return items.length > itemCount ? itemHeight * itemCount : items.length * itemHeight
    }, [items])

    return (
      <div
        onScroll={event => {
          var { scrollTop } = event.target
          setScroll(scrollTop)
          setOffset((scrollTop / itemHeight) >> 0)
        }}
        style={{
          height: `${containerHeight}px`,
          width: '60%',
          overflowY: 'auto',
          border: '1px dashed #ccc',
          margin: '80px auto',
          padding: '10px'
        }}>
        <ul style={
          {
            height: `${list.length * itemHeight}px`,
            position: 'relative',
            margin: '0px',
          }
        }>
          {
            items.map((value, index) => {
              return (
                <li
                  key={index}
                  style={
                    {
                      position: 'absolute',
                      top: `${index * itemHeight + scroll}px`,
                    }
                  }
                >
                  <span>{value}</span>
                </li>
              )
            })
          }
        </ul>
      </div>
    )
  }

  const initData = {
    listData: Array.from({ length: 99 }, (v, k) => k)
  }

  ReactDOM.render(
    <App {...initData} />,
    document.getElementById('root')
  )
</script>

Powered by VitePress.