主题
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 = true
,clearTimeout(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>
)
}
- [useMemo]虚拟滚动列表,在线预览
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>