Appearance
Basics
Here is how to start a full React‑Redux application in just a few lines with scalux, using a minimal counter example.
On this page you will learn how to:
- define an application's initial state
- connect a component to that state
- build the Redux store.
The counter
The example below shows how to set up a counter with:
- the initial state generated via
State - a connected component generated by
Component - the assembly of the Redux store with
registerandconfigureStore.
Step 1 : Defining the initial state and connecting the components
tsx
// src/app.tsx
import { State } from "scalux";
// Create the initial state and generate the builders
// The state must be an object (not a primitive value)
const { Component, register } = State({ count: 0 });
// Display component definition
type CounterProps = {
value: number;
increment: () => void;
decrement: () => void;
};
const Counter = ({ value, increment, decrement }: CounterProps) => (
<div>
<div>
<button aria-label="Increment value" onClick={() => increment()}>
Increment
</button>
<span>{value}</span>
<button aria-label="Decrement value" onClick={() => decrement()}>
Decrement
</button>
</div>
</div>
);
// Assemble the connected component
const CounterComponent: React.FC = Component({
domain: "Counter", // Action type prefix (e.g. "Counter/increment")
render: Counter,
data: (state) => ({ value: state.count }),
handlers: {
increment: (state) => ({ count: state.count + 1 }),
decrement: (state) => ({ count: state.count - 1 }),
},
});
// Register the handlers in Redux and generate the reducer.
// register must be called once all components have been defined.
const { reducer } = register();
export { CounterComponent, reducer };Step 2 : Creating the store
typescript
// src/store.ts
import { reducer } from "./app";
// scalux re‑exports redux‑toolkit’s configureStore
import { configureStore } from "scalux";
// Example of adding custom middleware
import { userDefinedMiddleware } from "./middlewares";
export const store = configureStore({
reducer,
middleware: (getDefaultMiddleWare) => getDefaultMiddleWare(),
});Step 3 : Using the component in the application
tsx
// src/main.tsx
// Re‑export of react‑redux’s Provider;
import ReactDOM from "react-dom/client";
import { Provider } from "scalux";
import { CounterComponent } from "./components";
import { store } from "./store";
ReactDOM.createRoot(
document.getElementById("root").render(
<Provider store={store}>
<CounterComponent />
</Provider>
)
);The remainder of this page explains each of these concepts in detail.
State
State, exported byscalux, defines the initial state from a serialisable data object.- The type of the state is inferred, and type annotations can be added to handle more complex situations (e.g. union types).
Component
The Component constructor returned by State connects a component to the application state.
It takes a configuration object with the following properties:
domain – A string used as a prefix for the generated action types. No action types have to be declared manually.
Example: The handlers
incrementanddecrementgenerate the action typesCounter/incrementandCounter/decrementrespectively.Note: The domain must be unique within the application. A duplicate will not go unnoticed: it triggers an initialisation error with a debug message to help pinpoint the source.
render – A functional React component whose prop types are essential.
scaluxseparates the component’s props by inference into two categories:- HandlerProps of typetypescripthandled by the
(payload: SomePayload) => void | () => voidhandlersproperty ofComponent. - DataProps – everything else, handled by the
dataproperty ofComponent.
- HandlerProps of type
data – Receives a selector whose first parameter is the state (typed by inference) and returns the
DataProps:typescript(state: RootState) => DataProps;handlers – For each
HandlerPropreceives a state‑mutation function (typed by inference) whose first parameter is the state, the second an optionalPayload, and which returns an object containing the state updates:typescript(state: RootState, payload: HandlerPayload) => Partial<InternalState>,In the example:
- The handlers
incrementanddecrementupdate thecountproperty. - Their payloads are
undefined(because the handlers are of type() => void). InternalStateis equal toRootStatehere; they diverge in advanced cases (e.g. history management), but the distinction will matter later.
Each handler returns a subset of the internal state containing only the modified properties.
If we had a handler prop{ doNothing: () => void }then{ doNothing: () => ({}) }would be valid.- The handlers
register
The register function registers all reducers generated by the component handlers.
⚠️ Important
- Call
registerafter all your connected components are defined viaComponent. - One (optional) way to ensure this is to call
registerfrom a file that re‑exports all components. - The reducer returned is then used to configure the Redux store.
For example:
typescript
// register all the handlers defined previously
const { reducer } = register();
// export the reducer to build the store
export { CounterComponent, reducer };Wrap‑up
In this section we have seen how to:
✅ define a minimalist state via State
✅ declare a component to render the model by separating the data (DataProps) from the handlers (HandlerProps)
✅ connect a component to the state and define its action types by assembling the render component with:
- a domain
- a data selector
- handlers
This simple approach covers common use cases. For more complex applications, scalux provides advanced tools that let you:
- split the state into slices, manage history and model state machines
- feed components with various kinds of selectors: hard‑coded data, selectors generated from the initial state, memoisation techniques, management of ownProps
- expose externalisation methods to avoid concentrating too many handlers in a single object.
