The name of refuse
comes from its main action: fuse
(join or blend) multiple components to form a single working app. Fun fact: it also means trash
, or refers to the action of being not willing to do something
in English)
Note: This library is a work-in-progress. Some features are not available yet. See Feature lists for more details.
Key points:
- Same modern API as React.js
- Full typescript support
- No JSX
- No build tool, no bundler, no transpiler required
Different from React:
- No class component supported
- No longer need to return single root element, so
Fragment
use cases now narrow down to key-ed diff only. - Ref works on component as well, and auto assign to the root element of the component.
- If the component have multiple root, it will point to the
DocumentFragment
, which will be empty (and useless) after moving its child to the DOM. Read more - (Don't need to wrap component in forwardRef, just) use the second parameter of component to assign it to a specific element.
- If the component have multiple root, it will point to the
- Component can return
number[]
,string[]
,fuse[]
or evenstring[][]
to render multiple elements.- Component can return
null
,undefined
,false
to skip rendering.
- Component can return
About syntax:
- Spread props:
<div ...${props}>
instead of<div {...props}>
- HTML's quotes now optional:
<div class=foo>
- Shorthand for component end-tags:
<${Foo}>bar<//>
- Comment:
<!-- comment -->
Syntax highlighting support:
- Intellij IDEA (WebStorm,...): supported by default
- VSCode: lit-html
Have you ever wondered how React.js works internally? Reading the source code of
refuse
is a good way to learn how it works.
npm i -S refusejs
import {useState, fuse,render} from "refusejs"
import type {RefuseComponent} from "refusejs"
interface Props {
someThing: string
}
const Component: RefuseComponent<Props> = (props, ref) => {
const [state, setState] = useState(0)
useEffect(() => {
const timer = setInterval(() => setState(state + 1), 1000)
return () => clearInterval(timer)
}, [])
return fuse`
<div class=foo ...${props} foo=${state}>
${props.children}
<${A}>Something...<//>
<button onclick=${() => setState(state + 1)}>Click me</button>
${state > 300 && fuse`<div>
Yeah
</div>`}
<!-- some comment -->
</div>
`
}
render(Component, document.getElementById('root'))
Or
<script src="https://unpkg.com/refusejs@latest/dist/refuse.umd.js"></script>
<script>
const {render, fuse} = Refuse;
render(
() => fuse`
<div>
<h1>Hello, world!</h1>
<p>It's a beautiful day.</p>
</div>
`,
document.getElementById('root2')
);
</script>
or
<script type="module">
import * as Refuse from 'https://unpkg.com/refusejs@latest/dist/refuse.modern.js'; // Support only modern browsers
// import * as Refuse from 'https://unpkg.com/refusejs@latest/dist/refuse.module.js'; // Support all browsers
Refuse.render(
() => Refuse.fuse`
<div>
<h1>Hello, world!</h1>
<p>It's a beautiful day.</p>
</div>
`,
document.getElementById('root')
);
</script>
- TypeScript
- Jsx to HyperScript using
htm
- render
- Custom components with props
- Custom components with children
- useState
- Automatic state update batching
- flushSync
- useEffect with cleanup
- Unmount components
- Component Tree
- Render: dirty mark, compare
- Render: keys
- Conditional rendering
- DOM diffing/patching
- useLayoutEffect
- useMemo, useCallback, useRef
- memo
- Fragment
- JSX Embedding Expression
- DOM Ref
- React.forwardRef
- Render multiple Refuse instances
- Error Boundary
- Concurrent Mode, useTransition
- useContext
- useReducer
- Test utils, write tests
- Portal
- Server-side rendering
- Synthetic Event
- Devtools, debugger, HMR - Hot reload
- Router
- useDeferredValue
- Suspense
- useImperativeHandle
- useDebugValue
- useId
- useSyncExternalStore
- Dynamic import, React.lazy
- Production build
- Profiler
- Linter
- Type checker on tagged template
- useEvent
Watch refuse
package
npm i --lockfile-only
npm link
npm run dev
Run example1
cd demo/example1
npm i --lockfile-only
npm link refusejs
npm run dev
And follow instructions.