4Geeks logo
4Geeks logo

Bootcamps

Explore our extensive collection of courses designed to help you master various subjects and skills. Whether you're a beginner or an advanced learner, there's something here for everyone.

Academy

Learn live

Join us for our free workshops, webinars, and other events to learn more about our programs and get started on your journey to becoming a developer.

Upcoming live events

Learning library

For all the self-taught geeks out there, here is our content library with most of the learning materials we have produced throughout the years.

It makes sense to start learning by reading and watching videos about fundamentals and how things work.

Full-Stack Software Developer - 16w

Data Science and Machine Learning - 16 wks

Search from all Lessons


LoginGet Started
← Back to Lessons

Weekly Coding Challenge

Every week, we pick a real-life project to build your portfolio and get ready for a job. All projects are built with ChatGPT as co-pilot!

Start the Challenge

Podcast: Code Sets You Free

A tech-culture podcast where you learn to fight the enemies that blocks your way to become a successful professional in tech.

Listen the podcast
  • Flux

  • ReactJS

Edit on Github

Learn What is React Flux

Why do we need Flux?

Remember how we always say that programming is like Taco Bell? It’s always the same ingredients, except differently! In this particular case, we are going to be relying heavily on Events to create our entire application architecture.

Why do we need Flux?

We know you are still in the process of learning React. States and props can be confusing, and now, with Flux, things are going to get a little bit harder. But it’s for a good cause!

Without Flux, you can’t create medium or big React applications because everything will become disorganized pretty quickly.

Also, two different views are not able to send data between each other like the components do (using props) because all views are siblings and React Router is instantiating them. We need to have a common store shared between all the views that we are going to call "The Store."

Here is a list of all the advantages of using it:

  • It centralizes and detaches the application data from the components: database communication and data-processing will not depend anymore on how the application looks (renders).
  • It controls the way your application data will flow: it does not matter if the data was inputted by the user or coming from a database; everything will be available in a clear and accessible way.
  • It differentiates your components in Views vs Re-usable components: your components will remain being abstracted from your application logic, making them 100% reusable for future applications.

React Flux

Flux Divides the Application in 3 Layers:

  
Views (Components)Every React Component that calls any Flux action is called a view. The reason to call those components differently is that React components are supposed to communicate with each other through their props (without Flux).

Once a React Component is hard-coded to Flux, you will not be able to reuse that component in the future (on this or any other development).
ActionsActions can be triggered by components (when the user clicks or interacts with the application) or by the system (for example, the auto-save functionality). Actions are the first step of any Flux workflow, and they always need to dispatch the store.
StoreThe store contains all the application data. It handles everything incoming from the dispatcher and determines the way data should be stored and retrieved.

Building a to-do list with flux.

1) Let's build a reducer that implements the flux pattern.

To take control over the flow of the data in our application we'll use a reducer to group the functions and the logic of the application (actions) along with the data that they handle and must be available to all the other components (state).

For now, let's just say that the reducer is a function that generates a new state every time it runs, and what it does depends on the information passed to the action function. This allows us to call the actions to update the state as indicates the flux pattern. To understand better how a reducer works, you can read this article where we cover this in more depth.

1// This is the reducer function 2const TaskReducer = (state, action) => { 3 // Depending on the action type, it performs a specific action 4 switch (action.type) { 5 case "add": 6 return [...state, action.payload]; 7 case "remove": 8 let newState=[...state] 9 newState.splice(action.index, 1); 10 return newState 11 default: 12 return state; 13 } 14};

The next step is to make this function available for all the other components of the application, for that we'll use a context with the hook useReducer, which will allow us to create a state and the actions function to publish it to the rest of the components.

1//TaskContext.jsx 2import { useReducer, createContext } from "react"; 3 4// Create an empty context 5const TaskContext = createContext(null); 6 7const TaskReducer = (state, action) => { 8 // Here is the reducer defined previously👆 9}; 10 11// Create a component to wrap our application within the context 12export function TaskProvider({ children }) { 13 // Create the state 'tasks' and the dispatcher 'taskActions' 14 // additionally we'll pass an empty array as an initial value. 15 const [tasks, taskActions ]= useReducer(TaskReducer, []); 16 return ( 17 {/* Create the context with our state and actions */} 18 <TaskContext.Provider value={{tasks, taskActions}}>{children}</TaskContext.Provider> 19 ); 20} 21 22// Is necessary to export the context for it to be used in other components. 23export default TaskContext;

Now the context is ready with our tasks, all we have to do is wrap our app with this component to start using our context.

1//index.jsx 2import React from "react"; 3import ReactDOM from "react-dom/client"; 4import App from "./App"; 5import { TaskProvider } from "./TaskContext.jsx"; 6 7ReactDOM.createRoot(document.getElementById("root")).render( 8 <React.StrictMode> 9 <TaskProvider> 10 <App /> 11 </TaskProvider> 12 </React.StrictMode>, 13); 14

The only thing left to do is to call the context in the components that need to use our tasks.

2) Let's start adding a new task

For that, we'll use a component that shows a textbox and a button that dispatches the adding action, all inside a form to ease the handle of the submit event.

All that is basic stuff, but since we want to use the context actions, we need to call it from within the component.

1import { tasks, useContext } from "react"; 2import TaskContext from "./TaskContext.jsx"; 3 4export default function AddItem() { 5 const { taskActions } = useContext(TaskContext); 6 // From this point onwards we have the reducer's actions available. 7 // ... 8}

To make use of these actions we call the function passing as a parameter an object with the properties of the action we want to call, being the most important the property type that indicates the specific action to execute. The rest of the properties are optional data that may be required by the action itself.

1// AddItem.jsx 2import { useContext } from "react"; 3import TaskContext from "./TaskContext.jsx"; 4 5export default function AddItem() { 6 // Using the context we access the 'taskActions' function. 7 const { taskActions } = useContext(TaskContext); 8 function handleAddTask(e) { 9 e.preventDefault(); 10 // Calling the action specifying the 'type' 11 // as well as task data that will be added. 12 let textbox = e.target.elements.task; 13 taskActions({ type: "add", payload: textbox.value }); 14 textbox.value = ""; 15 } 16 return ( 17 <li> 18 <form onSubmit={handleAddTask}> 19 <input name="task" type="text"/> 20 <button type="submit">+</button> 21 </form> 22 </li> 23 ); 24}

3) Now let's show the list

In the same manner that we access taskActions, we can also access the tasks object that contains the state of the list. Just like before, we must use the useContext hook in our component.

1import { useContext } from "react"; 2import "./App.css"; 3import TaskContext from "./TaskContext.jsx"; 4import ListItem from "./ListItem.jsx"; 5import AddItem from "./AddItem.jsx"; 6 7export default function App() { 8 // Accessing the context, but this time only using 'tasks' 9 const {tasks} = useContext(TaskContext); 10 11 return ( 12 <main> 13 <h2>Todo list</h2> 14 <ul className="list-group w-50"> 15 <AddItem /> 16 {tasks.map((task, index) => ( 17 <ListItem key={index} task={task} index={index} /> 18 ))} 19 </ul> 20 </main> 21 ); 22}

You can see that we are using the AddItem component where you can add a task. After that is being rendered the list with a map function, but a component ListItem is being used to show this element, and it also has the functionality to remove a task, let's check out that component.

4) Task deletion

Even though the render is very basic (a li element with the text and a button), what's interesting is how we delete an item with the actions.

I all stats with the user clicking the trash can icon. That's why we need to start our component by listening to the classic onClick event on the delete button,

1 onClick={() => taskActions({ type: "remove", index })}

We notice that the call to actions is similar to the one used to add items, but in this case is receiving a different parameter called index, which indicates to the dispatcher which elements are going to be deleted. Just like we saw in both examples, we can pass any data that's needed to the action at the moment of calling it, as additional parameters.

1import { useContext } from "react"; 2import TaskContext from "./TaskContext.jsx"; 3 4export default function ListItem({ task, index }) { 5 const { taskActions } = useContext(TaskContext); 6 7 return ( 8 <li> 9 {task} 10 <button 11 onClick={() => taskActions({ type: "remove", index })} 12 > 13 {/* Trash can icon */} 14 <i className="bi bi-trash3"></i> 15 </button> 16 </li> 17 ); 18}

Final result

We have implemented the logic of our application in a context applying the flux pattern, allowing its use in different components. Now we can see the final result working.