👻 Tiny React16 like library with Concurrent and Suspense.
- 🎉 Functional Component and hooks API
- 🎊 Concurrent and Suspense
- 🔭 keyed reconcilation algorithm
Fre (pronounced /fri:/
, like free) is a tiny and perfect js library, It means Free! ~
Fre has wonderful code, we need anyone to join us improve together.
Thanks for the following websites and sponsors, If you do the same, please tell us with issue~
Thanks for Rasmus Schultz
yarn add fre
import { h, render, useState } from 'fre'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
render(<Counter />, 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 Counter() {
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>
)
}
render(<Counter />, document.getElementById('root'))
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 Counter() {
const [state, dispatch] = useReducer(reducer, { count: 1 })
return (
<div>
{state.count}
<button onClick={() => dispatch({ type: 'up' })}>+</button>
<button onClick={() => dispatch({ type: 'down' })}>+</button>
</div>
)
}
render(<Counter />, document.getElementById('root'))
useEffect
takes two parameters, the first is a effect callback and the second is an array
if the array changed, the callback will execute after commitWork, such as pureComponentDidUpdate
if the array is empty, it means execute once, such as componentDidMount
if no array, it means execute every time , such as componentDidUpdate
if useEffect returns a function, the function will execute before next commitWork, such as componentWillUnmount
function Counter({ 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>
)
}
render(<Counter />, document.getElementById('root'))
useCallback
has the same parameters as useEffect
, but useCallback
will return a cached function.
const set = new Set()
function Counter() {
const [count, setCount] = useState(0)
const cb = useCallback(() => {
console.log('cb was cached')
}, [count])
set.add(cb)
return (
<div>
<h1>{set.size}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
useMemo
has the same parameters as useEffect
, but useMemo
will return a cached value.
function Counter() {
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>
)
}
render(<Counter />, document.getElementById('root'))
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>
}
Props are used for component communication
function App() {
const [sex, setSex] = useState('boy')
return (
<div>
<Sex sex={sex} />
<button
onClick={() => (sex === 'boy' ? setSex('girl') : setSex('boy'))}
/>
</div>
)
}
function Sex(props) {
return <div>{props.sex}</div>
}
Props contains children to render all the child elements of itself
const HelloBox = () => (
<Box>
<h1>Hello world !</h1>
</Box>
)
const Box = props => <div>{props.children}</div>
Hooks do not support HOC and extends, but render props are supported by default
const HelloBox = () => <Box render={value => <h1>{value}</h1>} />
const Box = props => <div>{props.render('hello world!')}</div>
Also can be render children
const HelloBox = () => (
<Box>
{value => {
return <h1>{value}</h1>
}}
</Box>
)
const Box = props => <div>{props.children('hello world!')}</div>
The default export h function needs to be configured
import { h } from 'fre'
{
"plugins": [["transform-react-jsx", { "pragma": "h" }]]
}
If browser environment, recommend to use htm
Fre implements a tiny priority scheduler, which like react Fiber.
It can break the work, and when there are idle time, the work will continue.
Concurrent Mode is also called time slicing
or concurrent mode
.
Suspense is another way to break the work.
It throws promise, and fre catches promise then suspend the work. It waits until resolve to the promise.
Fre implements a compact reconcilation algorithm support keyed, which also called diff.
It uses hash to mark locations to reduce much size.