rescript-chai
v0.2.0
Published
<div style="text-align: center; margin-bottom: 2rem;" align="center"> <img src="./logo.png" alt="Chai Logo" style="width: 200px; height: auto;" /> </div>
Readme
[!WARNING] Chai is currently in early development. Some APIs are incomplete, unstable, or subject to change. Do not use Chai in production.
Here's an example of a file Brew.res that defines the core logic for a Model-View-Update loop:
// Brew.res
// Define your model - the state of your component
type model = { count: int }
// Define your messages - events that can change state
type msg = Increment | Decrement | Set(int)
// Define your commands - effects that interact with the world
type cmd = NoOp | Log(Cmd.Log.t)
// The update function - pure, handles all state changes
let update = (model, msg) => switch msg {
| Increment => ({ count: model.count + 1 }, NoOp)
| Decrement => ({ count: model.count - 1 }, NoOp)
| Set(n) => ({ count: n }, Log("Set count to" ++ string_of_int(n)))
}
// Handle your side effects (HTTP, storage, timers, etc.)
// Delegate to Chai runtime defaults (Cmd.Log.run) or make your own
// (Swap out runners for test and production environments!)
let run = async (cmd, dispatch) => switch cmd {
| NoOp => ()
| Log(c) => await c->Cmd.Log.run
}
// Subscriptions for external events (WebSocket, timers, etc.)
let subs = (model) => [
// while condition true, do ...
Sub.Time.every(model.count < 30, {
interval: 1000,
cons: _ => Increment,
})
]
// Describe the initial state and effects
let init = (
{
count: 0
},
Log("Counter initialized")
)
// Use zustand middleware or make your own!
// Compatible with redux devtools :)
// https://github.com/reduxjs/redux-devtools
let middleware = (store) => store
->Chai.persist({name: "counter"})
->Chai.devtools({})
// Wire everything together!
let useCounter = Chai.brew({
update, run, subs, init, middleware, opts: {
// Undo/Redo/Reset functionality
chrono: { enabled: true }
}
})After you've created your hook, you can then use it anywhere you want. The generated hook is idempotent and will never re-run effects. You can safely call it from any component to access the core loop's state:
// Counter.res
open Chai
@react.component
let make = () => {
// `useCounter` is idempotent -
// use this hook anywhere to tap into the core MVU loop
// without fear of re-running effects
let (state, dispatch, _) = Brew.useCounter()
<div>
<h2>{React.string(state.title)}</h2>
<p>{React.string("Count: " ++ Int.toString(state.count))}</p>
<button onClick={_ => Increment->dispatch}>
{React.string("Inc")}
</button>
</div>
}Because Chai uses Zustand under the hood, granular reactivity has never been easier. select delegates to Zustand and only re-renders when the selected projection changes.
npm install rescript-chai