👻 Fast 1kB React like library with the same hooks API
- 🎉 FunctionalComponent and hooks API
- 🎊 Async rendering like react Fiber(also called time slicing, concurrent mode)
- 🔭 keyed reconcilation(also called diff) algorithm
Fre (pronounced /fri:/
, like free) is a tiny and perfect js library, It means Free! ~
Package | Version | About |
---|---|---|
Fre |
fre core | |
Fard |
mini-program with fre | |
use-routes |
router for fre |
cd demo
yarn install
yarn start // run use cases for API
yarn concurrent // run demo for concurrent mode (fiber scheduler)
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, usually props
When the array changes, the effect callback will run after commitWork, such as pureComponentDidUpdate
if the array is empty, it means use once, such as componentDidMount
if the second is undefined, it means use every time , such as componentDidUpdate
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'))
functionalComponent is a new components scheme
function App() {
const [count, setCount] = useState(0)
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
<Sex count={count} />
</div>
)
}
function Sex(props) {
const [sex, setSex] = useState('boy')
return (
<div>
<h2>{props.count}</h2>
<h1>{sex}</h1>
<button
onClick={() => {
sex === 'boy' ? setSex('girl') : setSex('boy')
}}
>
x
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
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>
Does not support HOC but you can renturn a function wrapped another function.
function App() {
return HOC(() => <div>I am wrapped by a HOC</div>)
}
If you want to rewrite any function, please use options, such as:
options.end = false
options.commitWork = fiber => {
// something you will rewrite commitWork
}
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 supports most JSX syntax, if-else
is also Ok but need to be careful.
{
isShow && <A />
isShow ? <A /> : null
isShow ? <A /> : <B />
}
because there no key
for them, please use it as late as possible.
Fre implement a tiny priority scheduler, which like react Fiber.
Async rendering is also called time slicing
or concurrent mode
.
Fre implements a compact diff algorithm support keyed
It uses hash to mark locations for easy comparison
MIT ©132yse inspired by anu