👻 Tiny React16 like library with Concurrent and Suspense.
- 🎉 Functional Component and hooks API
- 🎊 Concurrent and Suspense
- 🔭 keyed reconcilation algorithm
Fre has wonderful code, we need more to join us and improve together.
Thanks for the following websites and sponsors, If you do the same, please tell us with issue~
Any other demos click here
yarn add fre
import { h, render, useState } from 'fre'
function App() {
const [count, setCount] = useState(0)
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
render(<App />, document.getElementById('root'))
useState
is a base API, It will receive initial state and return a Array
You can use it many times, new state is available when component is rerender
function App() {
const [up, setUp] = useState(0)
const [down, setDown] = useState(0)
return (
<div>
<h1>{up}</h1>
<button onClick={() => setUp(up + 1)}>+</button>
<h1>{down}</h1>
<button onClick={() => setDown(down - 1)}>-</button>
</div>
)
}
useReducer
and useState
are almost the same,but useReducer
needs a global reducer
function reducer(state, action) {
switch (action.type) {
case 'up':
return { count: state.count + 1 }
case 'down':
return { count: state.count - 1 }
}
}
function App() {
const [state, dispatch] = useReducer(reducer, { count: 1 })
return (
<div>
{state.count}
<button onClick={() => dispatch({ type: 'up' })}>+</button>
<button onClick={() => dispatch({ type: 'down' })}>+</button>
</div>
)
}
It is the execution and cleanup of effects, which is represented by the second parameter
useEffect(f) // effect (and clean-up) every time
useEffect(f, []) // effect (and clean-up) only once in a component's life
useEffect(f, [x]) // effect (and clean-up) when property x changes in a component's life
function App({ flag }) {
const [count, setCount] = useState(0)
useEffect(() => {
document.title = 'count is ' + count
}, [flag])
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
If it return a function, the function can do cleanups:
useEffect(() => {
document.title = 'count is ' + count
reutn () => {
store.unsubscribe()
}
}, [])
More like useEffect, but useEffect queue in requestAnimationFrame
, but useLayout is sync and block commitWork.
useLayout(() => {
document.title = 'count is ' + count
}, [flag])
useMemo
has the same parameters as useEffect
, but useMemo
will return a cached value.
function App() {
const [count, setCount] = useState(0)
const val = useMemo(() => {
return new Date()
}, [count])
return (
<div>
<h1>
{count} - {val}
</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
useCallback
is based useMemo
, it will return a cached function.
const cb = useCallback(() => {
console.log('cb was cached')
}, [])
The implement amount to
useMemo(() => cb, deps)
useRef
will return a function or an object.
function App() {
useEffect(() => {
console.log(t) // { current:<div>t</div> }
})
const t = useRef(null)
return <div ref={t}>t</div>
}
If it use a function, It can return a cleanup and exectes when removed.
function App() {
const t = useRef(dom => {
if (dom) {
doSomething()
} else {
cleanUp()
}
})
return flag && <span ref={t}>I will removed</span>
}
There are some awesome APIs, It used outside of component, Usually a with
prefix is used.
Simplify context implement for hooks, no need Provider or useContext, share state easier.
const useTheme = withContext('light')
function App() {
const [theme, setTheme] = useTheme()
return (
<div>
{theme}
<A />
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
change
</button>
</div>
)
}
function A() {
const [theme] = useTheme()
return <div>{theme}</div>
}
One API to suspense not only fetch data but also dynamic component.
const useUser = withSuspense(pageSize =>
fetch(`https://api.clicli.us/users?level=4&page=1&pageSize=${pageSize}`)
.then(res => res.json())
.then(next => next.users)
)
const OtherComponent = withSuspense(() => import('./other-component'))
function App() {
const users = useUser(pageSize)
return <OtherComponent users={users} />
}
const HelloBox = () => <Box render={value => <h1>{value}</h1>} />
const Box = props => <div>{props.render('hello world!')}</div>
const HelloBox = () => (
<Box>
{value => {
return <h1>{value}</h1>
}}
</Box>
)
const Box = props => <div>{props.children('hello world!')}</div>
Fre implements a tiny priority scheduler, which called Concurrent Mode.
It uses the linked list
data struct to iterate a tree, which can better break, continue, and fallback.
At the same time, it uses double buffering to separate reading and writing.
Of course, the new data struct brings different algorithms and many possibilities.
Time slicing is the scheduling of reconcilation, synchronous tasks, sacrifice CPU and reduce blocking time
Suspense is the scheduling of promise, asynchronous tasks, break current tasks, and continue tasks after promise resolve
Fre implements a compact reconcilation algorithm support keyed, which also called diff.
It uses hash to mark locations to reduce much size.