Rate Questions with React and Redux - A baby example 👶
https://unsplash.com/photos/s0XDLfhyN34
I will build a small application for simply rating questions. This is designed as an exercise project for React and Redux, since I am still not understanding it.
âž¡ï¸ Github Repo is available here ⬅ï¸
📄 Table of contents
- Motivation for this article
- Modularizing the base
- Adding Redux
- Add another component in the Redux App
- Implement ducks
- Chrome Redux DevTools
"Everything is practice." - Pele
Motivation for this article
Another small application to understand Redux and React. It feels like it's the 100th app trying to grasp Redux. But 1 month without Redux and you start at basically nothing again. I am like: "Yeah I have heard about that" - and that's it. Action, Action Creators, Reducers, Dispatch, blabla. Too many things to understand :D So once again ↗ï¸
Modularizing the base
Structure the components in order to fit perfectly into a Redux application.
âž¡ï¸ Codebase on Github ⬅ï¸
- the stopwatch component has it's own local state is not dependent on other components
- the stats and counter components are dependent on other components
- the AddQuestionForm is dependent on other components and also contains logical information
- the header and question components
Modularizing helps to
- isolate responsibilities, which means easier testing and debugging
- better scale the app and easier for the use of Redux
- better organize between teams
âž¡ï¸ Modularized Code on Github ⬅ï¸
Adding Redux
Action Types
Decide which components should take part in the Redux store.
-> In this application only the questions have to be made available to all components.
Find what events happen in your application for this specific state. -> In this application it is
- changing the score
- adding questions
- removing questions
Reducers
Reducers are pure functions, that change state according to the action type.
The reducer function provides different switch statements on how change the state. (Make sure to never change the state itself! It should be a pure function! #immutability)
For example:
export default function Player(state = initialState, action) {
switch (action.type) {
case QuestionActionTypes.ADD_PLAYER:
return [
...state,
{
name: action.name,
score: 0,
},
];
case QuestionActionTypes.REMOVE_QUESTION:
return [...state.slice(0, action.index), ...state.sclice(action.index + 1)];
case QuestionActionTypes.UPDATE_QUESTION_SCORE:
return state.map((question, index) => {
if (index === action.index) {
return {
...question,
score: question.score + question.score,
};
}
return question;
});
default:
return state;
}
}
Actions and Action Creators
Submiting an action to Redux
- action creators generate an action (action = an event that will result in a change in state)
- action is dispatched to the Redux store
- a reducer passes the action to a component and returns the new state
For example for adding a question:
export const addQuestion = name => ({
type: QuestionActionTypes.ADD_QUESTION,
name,
});
Create the Redux Store
Create a store in your index.js passing it the main reducer and wrap it around your scoreboard component in order to provide the store to the whole application.
Connect the container to the store
- use
mapStateToProps
to assign the state to a prop value -> assign the state of the questions as props - for automatically dispatching actions, that are created use:
const {dispatch, questions} = this.props;
const addQuestion = bindActionCreators(QuestionActionCreators.addQuestion, dispatch);
const removeQuestion = bindActionCreators(QuestionActionCreators.removeQuestion, dispatch);
const updateQuestionScore = bindActionCreators(QuestionActionCreators.updateQuestionScore, dispatch);
- update the event handlers on the components accordingly (counter, question and scoreboard components)
- the header and stopwatch components don't need changes, because they do not participate in the Redux cycle
Add another component in the Redux App
Now we want to display details to each question
- add a new action type (select a question)
- extend the reducer with a new switch case and additional state
- add a new action creator for selecting a question
- create a new bindActionCreator in the scoreboard component
- update mapStateToProps with the selected question index
- create a QuestionDetail component to display details
- update the event handler on the question component
âž¡ï¸ See the commit with the implementation of the detail component on Github ⬅ï¸
Implement ducks
For smaller apps the ducks concept can help to develop a Redux application faster. Basically instead of keeping everything modular (actions, reducers, actionCreators), we can also keep them in one file to have a better overview.
This file looks like:
// Actions
const ADD_QUESTION = 'question/ADD_QUESTION';
const REMOVE_QUESTION = 'question/REMOVE_QUESTION';
const UPDATE_QUESTION_SCORE = 'question/UPDATE_QUESTION_SCORE';
const SELECT_QUESTION = 'question/SELECT_QUESTION';
// Reducers
const initialState = {
questions: [
{
name: 'Do you like AI?',
score: 31,
created: '00:00',
updated: '00:00',
},
{
name: 'Do you like Engineering?',
score: 20,
created: '00:00',
updated: '00:00',
},
{
name: 'How many Redux Apps?',
score: 50,
created: '00:00',
updated: '00:00',
},
],
selectedQuestionIndex: -1,
};
export default function Question(state = initialState, action) {
const date = `${new Date().getHours()}:00`;
switch (action.type) {
case ADD_QUESTION:
const addQuestionList = [
...state.questions,
{
name: action.name,
score: 0,
created: date,
},
];
return {
...state,
questions: addQuestionList,
};
case REMOVE_QUESTION:
const removeQuestionList = [
...state.questions.slice(0, action.index),
...state.questions.slice(action.index + 1),
];
return {
...state,
questions: removeQuestionList,
};
case UPDATE_QUESTION_SCORE:
const updateQuestionList = state.questions.map((question, index) => {
if (index === action.index) {
return {
...question,
score: question.score + action.score,
updated: date,
};
}
return question;
});
return {
...state,
questions: updateQuestionList,
};
case SELECT_QUESTION:
return {
...state,
selectedQuestionIndex: action.index,
};
default:
return state;
}
}
// ActionCreators
export const addQuestion = name => ({
type: ADD_QUESTION,
name,
});
export const removeQuestion = index => ({
type: REMOVE_QUESTION,
index,
});
export const updateQuestionScore = (index, score) => ({
type: UPDATE_QUESTION_SCORE,
index,
score,
});
export const selectQuestion = index => ({
type: SELECT_QUESTION,
index,
});
âž¡ï¸ See the commit with the implementation of ducks on Github ⬅ï¸
Chrome Redux DevTools
- Download the Redux DevTools Extension
- add the necessary line of code to your store
const store = createStore(
QuestionReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
);
The DevTools help to develop and debug your Redux app. Check out this article for more.
âž¡ï¸ Result on Github ⬅ï¸
If you gained something from this article let me know with a comment or heart. Make sure to follow for more :)
Top comments (0)