Getting Started
This section will walk you through the main features of re-reduced
with practical examples:
In the following examples we'll be building a simple counter app.
Defining a type system
[root]/examples/Counter/types.ts
export interface State {counter: number;}
Creating Actions
[root]/examples/Counter/actions.ts
import { createAction } from "re-reduced";export default {adjust: createAction<number>("ADJUST"),decrement: createAction("DECREMENT"),increment: createAction("INCREMENT"),};
alternatively, actions can be created in bulk using createActions
Note that, when using createActions, the action type is infered from the action name and namespace
import { createActions } from "re-reduced";export default createActions("COUNTER", create => ({adjust: create.action<number>(),decrement: create.action(),increment: create.action(),}));
Creating Reducers
[root]/examples/Counter/reducers.ts
import { combineReducers } from "redux";import { createReducer, match } from "re-reduced";import actions from "./actions";import { State } from "./types";const INITIAL_STATE = 0;export const counter = createReducer<number>([actions.increment.reduce(state => state + 1),actions.decrement.reduce(state => state - 1),actions.adjust.reduce((state, payload) => state + payload),],INITIAL_STATE);// which can also be written like:export const counter = createReducer<number>([match(actions.increment, state => state + 1),match(actions.decrement, state => state - 1),match(actions.adjust, (state, payload) => state + payload),],INITIAL_STATE);export default combineReducers<State>({counter,});
Creating selectors
Selectors
in redux are functions defined in terms of the application state and perform derivation
and aggregation
.
reselect
is the recommended library for selector composition.
import { createSelector } from "reselect";import { State } from "./types";export const getCounter = (state: State) => state.counter;export const getCounterIsOdd = createSelector(getCounter,counter => counter % 2 !== 0);export const getCounterIsPositive = createSelector(getCounter,counter => counter >= 0);
Connecting a component
Re-reduced provides the connectWithActions
helper function for easily connecting state and actions to a component.
[root]/src/ui/containers/Counter.tsx
import * as React from "react";import { connectWithActions } from "re-reduced";import actions from "./actions";import * as selectors from "./selectors";interface Props {count: number;isOdd: boolean;isPositive: boolean;actions: typeof actions;}const Counter = (props: Props) => (<section><button onClick={() => props.actions.decrement()}>-1</button><button onClick={() => props.actions.adjust(-5)}>-5</button><buttondisabledstyle={{color: props.isPositive ? (props.isOdd ? "blue" : "green") : "red",fontWeight: "bold",paddingLeft: 5,paddingRight: 5,width: 50,}}>{props.count}</button><button onClick={() => props.actions.adjust(5)}>+5</button><button onClick={() => props.actions.increment()}>+1</button></section>);const enhance = connectWithActions<Props>(actions, {count: selectors.getCounter,isOdd: selectors.getCounterIsOdd,isPositive: selectors.getCounterIsPositive,});export default enhance(Counter);
Note that
connectWithActions
does not provide 100% feature parity with react-redux's connect.
The above enhance
example can be easily rewritten without connectWithActions
, using connect
, applySpec
and bindActionCreators
.
import { bindActionCreators } from "redux";import { connect } from "react-redux";import { applySpec } from "ramda";const enhance = connect(applySpec({count: selectors.getCounter,isOdd: selectors.getCounterIsOdd,isPositive: selectors.getCounterIsPositive,}),dispatch => ({actions: bindActionCreators(actions, dispatch),}));
Alternatively, from version 1.5.0
, re-reduced supports hooks, using the following syntax:
import { useActions, useReduxState } from "re-reduced";import actions from "./actions";import * as selectors from "./selectors";function Counter() {const { decrement, increment, adjust } = useActions(actions);const { count, isOdd, isPositive } = useReduxState({count: selectors.getCounter,isOdd: selectors.getCounterIsOdd,isPositive: selectors.getCounterIsPositive,});return <div>{/* app render logic... */}</div>;}
Demo
Using hooks: