Hello all! In this post I will try to explain the way I use Redux with Typescript. I used to use Connect API for Redux but with the Hooks API, it became my new favourite. So, I decided to create such article with a simple example.
The goal of this example is to create an app with two colour palettes to let you chose the background colour and text colour of the display panel.
Let’s start with creating our app with built in Redux + Typescript template.
npx create-react-app my-app --template redux-typescript
This way of app creation comes with an example of Counter app, you can remove Counter folder in ‘src/features’ also remove mentions of Counter in ‘src/app/store.ts’ and ‘src/App.tsx’.
Now our App.tsx file should look like;
// App.tsx
import './App.css';
const App = () => (
<div className="App">
</div>
);
export default App;
Now let’s take a look at automatically created app folder, which includes two files.
- hooks.ts
- store.ts
// hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// Use throughout your app instead of plain
//`useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
With Hooks API, we handle dispatching and getting the data with useDispatch and useSelector hooks. As you can see, boilerplate also gives us the types of such hooks for Typescript. Using useAppDispatch and useAppSelector instead of plain react-redux hooks with declared types helps you not to repeat the RootState everytime in your reducer functions.
// store.ts
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
export const store = configureStore({
reducer: {
<reducer_name>: <reducer>,
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
In store.ts, if you want to add a reducer, all you need to do is importing it in here and add it to the configureStore’s reducer object.
RootState is the state of your whole application, it includes all your reducers. You will understand it better when we create our colorPaletteReducer.
colorPaletteReducer
First let’s decide the model of our reducer. As I mentioned above, we need 2 colours, for background and for text. Also we are going to import createSlice, PayloadAction and RootState. Our model is;
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '../app/store';
interface ColorPaletteStateModel {
bgColor: string;
textColor: string;
};
Our reducer name;
const reducerName = 'colorPalette';
Now it is time to define our initialState
const initialState : ColorPaletteStateModel = {
bgColor: '',
textColor: '',
};
createSlice function takes an object with 3 attributes.
- name
- initialState
- reducers
We already defined initialState and name for our slice. Let’s add the reducers for setting background color and setting text color.
const colorPaletteSlice = createSlice({
name: reducerName,
initialState,
reducers: {
setBgColor: (state, action: PayloadAction<string>): ColorPaletteStateModel => ({
...state,
bgColor: action.payload
}),
setTextColor: (state, action: PayloadAction<string>): ColorPaletteStateModel => ({
...state,
textColor: action.payload
}),
}
});
Now we have reducer slice with actions to set the state. What we need next is, getter functions to read the state from the components.
// selectors
const getBgColor = (state: RootState): string => state[reducerName].bgColor;
const getTextColor = (state: RootState): string => state[reducerName].textColor;
Voila! Yes, it’s that easy 🙂 Now let’s put all together and see our colorPalette.redux.ts file.
// colorPalette.redux.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '../app/store';
export interface ColorPaletteStateModel {
bgColor: string;
textColor: string;
};
export const reducerName = 'colorPalette';
const initialState : ColorPaletteStateModel = {
bgColor: '',
textColor: '',
};
const colorPaletteSlice = createSlice({
name: reducerName,
initialState,
reducers: {
setBgColor: (state, action: PayloadAction<string>): ColorPaletteStateModel => ({
...state,
bgColor: action.payload
}),
setTextColor: (state, action: PayloadAction<string>): ColorPaletteStateModel => ({
...state,
textColor: action.payload
}),
}
});
// selectors
const getBgColor = (state: RootState): string => state[reducerName].bgColor;
const getTextColor = (state: RootState): string => state[reducerName].textColor;
const { setBgColor, setTextColor } = colorPaletteSlice.actions;
export {
getBgColor,
setBgColor,
getTextColor,
setTextColor
};
export default colorPaletteSlice.reducer;
Now we will go back to the store.ts file and add our reducer to the store.
// store.ts
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import colorPaletteReducer from '../redux/colorPalette.redux';
export const store = configureStore({
reducer: {
colorPalette: colorPaletteReducer,
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
Now we have fully functioning store, reducers and actions. All we need to do is create some components and use our reducers.
For choosing colors I will use react-color-palette, you need to add it via yarn or npm.
// for yarn
yarn add react-color-palette
// for npm
npm install react-color-palette --save
We need 2 components for this app, ColorPalette and DisplayPanel
ColorPalette.tsx
import { useEffect } from 'react';
import { ColorPicker, useColor } from "react-color-palette";
import { setBgColor, setTextColor } from '../../redux/colorPalette.redux';
import { useAppDispatch } from '../../app/hooks';
import "react-color-palette/lib/css/styles.css";
const ColorPalette = () => {
const [textColor, setColor] = useColor("hex", "#ffffff");
const [bgColor, setBackgroundColor] = useColor("hex", "#000000");
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(setBgColor(bgColor.hex));
},[bgColor, dispatch]);
useEffect(() => {
dispatch(setTextColor(textColor.hex));
},[textColor, dispatch]);
return (
<div>
<div>
<h2>Set The Text Color From Here</h2>
<ColorPicker width={456} height={228} color={textColor} onChange={setColor} hideHSV dark />
</div>
<div>
<h2>Set The Background Color From Here</h2>
<ColorPicker width={456} height={228} color={bgColor} onChange={setBackgroundColor} hideHSV dark />
</div>
</div>
);
};
export default ColorPalette;
As you can see above, we dispatch the state in useEffect hooks. If you need more information on React Hooks, you can read my post about it.
DisplayPanel.tsx
import { getBgColor, getTextColor } from '../../redux/colorPalette.redux';
import { useAppSelector } from '../../app/hooks';
const DisplayPanel = () => {
const textColor = useAppSelector(getTextColor);
const bgColor = useAppSelector(getBgColor);
const styles = {
backgroundColor: bgColor,
color: textColor,
height: '200px',
width: '200px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
return <div style={styles}>See The Changes Here</div>;
};
export default DisplayPanel;
All we needed to do here, getting the state via useAppSelector hook, use the state in the way we want.
And lastly, let’s add the new components to our App.tsx and run the application.
import ColorPalette from './features/colorPalette';
import DisplayPanel from './features/displayPanel';
import './App.css';
const styles = {
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
}
const App = () => (
<div className="App" style={styles}>
<ColorPalette />
<DisplayPanel />
</div>
);
export default App;
Now it’s all ready and let’s look at our app on browser.
I realize it has been a long post, thank you for your patience! If you have any questions or comment, feel free contact me!
Post by Oz @typescript @reactjs
Tweet