React, by Facebook, is a very nice library for creating user interfaces. The only problem is that React doesn’t care how your application handles the data. Most people use React as the V in MV*. So, Facebook introduced a pattern called Flux which brings a functional approach to data handling inside an app. This tutorial gives a brief introduction on the Flux pattern and shows how to create a note taking app using React and Flux architecture.
A Primer on Flux
Flux relies on unidirectional data flow. We have two key components in the Flux pattern:
- Stores : A store component, as the name suggests, stores the application data.
- Actions : New data flows into the stores through actions. Stores listen to actions and do some tasks (e.g. modify data) when actions are invoked. This keeps the data flow unidirectional.
To reinforce the concept let’s take a real world example. For example, in a note making app you can have the following arrangement:
- A store called
NoteStore
which stores a list of notes. - You can have an action called
createNote
. The storeNoteStore
listens to the actioncreateNote
and updates its list with a new note whenever the action is invoked. Data flows into the store only through actions. - The
NoteStore
triggers an event whenever its data changes. Your React component, sayNoteListComponent
, listens to this event and updates the list of notes presented on the view. This is how the data flows out of the store.
So, the data flow can be visualised as following :
The biggest advantage of the Flux pattern is that it keeps your application data flat. As mutation can be done only through actions, it’s easier to understand how the data change affects the whole application.
Note:
If you have gone through Facebook’s guide to Flux, you might have noticed the concept of a Dispatcher. A Dispatcher is a registry of callbacks into the stores. When an action is invoked, the Dispatcher responds to it and sends the associated data to all the registered stores. Stores then check the action type and perform tasks accordingly.
The above process has been greatly simplified by a library called Reflux . It removes the concept of Dispatchers by making the actions listenable. So, in Reflux stores can directly listen to actions and respond to their invocation.
To understand the Flux pattern fully let’s build a simple note taking app with Reflux, React, and Node.js.
Setting Up a Development Environment
We will use use React and Reflux as Node modules and use Browserify to make them available on the client side as well. So, here is how we set up the environment:
- We will use Browserify to bundle up our React components, Actions and Stores into a client side
.js
package. - We will use
grunt watch
to detect changes in the above components and re-run Browserify every time a change occurs. grunt nodemon
is used to restart the server whenever any.jsx
or.js
file is changed so that you don’t have to do it manually.
You can download the code from GitHub and open up Gruntfile.js
to read about the tasks. Once you have the repo on your machine you can just run npm install
to install the required node modules. Run the following commands and start development:
grunt watch
grunt nodemon
The app is accessible at https://localhost:8000
and works as following:
Working on the App
Let’s start with various components of the app. Here’s how we can divide our UI into various components:
Here is what each component does:
NoteApp
: This is the root component that comprises of two child components:NoteListBox
andNoteCreationBox
.NoteListBox
: Has a single child componentNoteList
. It retrieves a list of notes from Flux Store and passes them toNoteList
.NoteList
: Responsible for rendering eachNote
component. Passes a note object to eachNote
component.Note
: Displays the details for a single note item. In this case we just displaytitle
. You can easily go ahead and display other details likedate
,subtitle
etc.NoteCreationBox
: This component renders aTextArea
component and passes currently-edited noteid
to it, if any.TextArea
: Provides atextarea
to accept user input. Passes the note text toNoteCreationBox
for saving.
Creating Actions
Let’s use Reflux to create some actions. If you open up actions/NoteActions.js
, you can see how actions are created. Here is the snippet:
var Reflux = require('reflux');
var NoteActions = Reflux.createActions([
'createNote',
'editNote'
]);
module.exports = NoteActions;
Reflux.createActions
is used to create Actions. We export these Actions in order to use them in our components.
Creating Store
We have a single store called NoteStore
which maintains an array of notes. The following code is used to create the store (stores/NoteStore.js
) :
var Reflux = require('reflux');
var NoteActions = require('../actions/NoteActions');
var _notes = []; //This is private notes array
var NoteStore = Reflux.createStore({
init: function() {
// Here we listen to actions and register callbacks
this.listenTo(NoteActions.createNote, this.onCreate);
this.listenTo(NoteActions.editNote, this.onEdit);
},
onCreate: function(note) {
_notes.push(note); //create a new note
// Trigger an event once done so that our components can update. Also pass the modified list of notes.
this.trigger(_notes);
},
onEdit: function(note) {
// Update the particular note item with new text.
for (var i = 0; i < _notes.length; i++) {
if(_notes[i]._id === note._id) {
_notes[i].text = note.text;
this.trigger(_notes);
break;
}
}
},
//getter for notes
getNotes: function() {
return _notes;
},
//getter for finding a single note by id
getNote: function(id) {
for (var i = 0; i < _notes.length; i++) {
if(_notes[i]._id === id) {
return _notes[i];
}
}
}
});
module.exports = NoteStore; //Finally, export the Store
As you see we listen to two actions, createNote
and editNote
, inside the init
method. We also register callbacks to execute when actions are invoked. The code for adding/updating a note is pretty straightforward. We also expose getters to retrieve list of notes. Finally, the store is exported so that it can be used in our component.
Creating Components
All our React components are located in the react/components
directory. I have already shown the overall structure of the UI. You can check out the downloaded source code to know more about each component. Here I will show you the key thing (i.e. how our components invoke actions and interact with the store).
NoteListBox:
This component obtains a list of notes from NoteStore
and feeds them to NoteList
component which then renders the notes. Here is how the component looks like:
var React = require('react');
var NoteList = require('./NoteList.jsx');
var NoteStore = require('../../stores/NoteStore');
var NoteListBox = React.createClass({
getInitialState: function() {
return { notes: NoteStore.getNotes() };
},
onChange: function(notes) {
this.setState({
notes: notes
});
},
componentDidMount: function() {
this.unsubscribe = NoteStore.listen(this.onChange);
},
componentWillUnmount: function() {
this.unsubscribe();
},
render: function() {
return (
<div className="col-md-4">
<div className="centered"><a href="" onClick={this.onAdd}>Add New</a></div>
<NoteList ref="noteList" notes={this.state.notes} onEdit={this.props.onEdit} />
</div>
);
}
});
module.exports = NoteListBox;
When the component mounts we start listening to the NoteStore
‘s change
event. This is broadcast whenever there is a mutation in the notes list. Our component listens to this event so that it can re-render the notes in case of any change. The following line registers a listener:
this.unsubscribe = NoteStore.listen(this.onChange);
So, whenever there is a change onChange
method of the component is called. This method receives an updated note list and changes the state.
this.setState({
notes: notes //state changes
});
As this.state.notes
is passed as a prop
to NoteList
, whenever the state changes NoteList
re-renders itself.
Finally, we write this.unsubscribe()
inside componentWillUnmount
to remove the listener.
So, this is how NoteList
always stays up-to-date by listening to Store’s change
event. Now let’s see how a note is created/edited.
NoteCreationBox:
Have a look at the following method of NoteCreationBox
:
handleSave: function(noteText, id) {
if (id) {
NoteActions.editNote({ _id: id, text: noteText });
} else {
NoteActions.createNote({ _id: Date.now(), text: noteText });
}
}
This method is called whenever the Save button is clicked. It accepts noteText
as its first parameter. If an id
is passed as the second parameter, we know this is an edit operation and invoke the action NoteActions.editNote()
. Otherwise we generate an id
for the new note and call NoteActions.createNote()
. Remember our NoteStore
listens to these actions. Depending on the action appropriate store callback is executed. Once the data is mutated the store triggers a change event and our component NoteList
updates itself.
This is how the data flows into the system and subsequently goes out in a Flux based application.
Why Use React on the Server
You might be wondering why I used React and Reflux on the server. One of the cool features of React is that the components can be rendered on both the client and server. Using this technique, you can create isomorphic apps that render on server and also behave as Single Page Apps. While this may not be required for a note app, you can easily use this setup to build complex isomorphic apps in future.
I encourage you to go through the source code and improve it further as there is a lot of room for improvements. If you have any questions do let me know in comments.
Thanks for reading!
Frequently Asked Questions about Creating a Note-Taking App with React and Flux
What is the role of Flux in creating a note-taking app with React?
Flux is a design pattern that Facebook uses internally with React. It complements React’s composable view components by utilizing a unidirectional data flow. In the context of creating a note-taking app, Flux plays a crucial role in managing the data flow. It ensures that data moves in a single direction, from actions to stores, and then to the views. This makes the app more predictable and easier to understand, as it avoids the complexity of data sharing between components and the confusion of two-way data binding.
How does React work with Flux in building a note-taking app?
React and Flux work together to build a note-taking app by dividing responsibilities. React is responsible for rendering views and responding to user inputs, while Flux manages the application’s data flow. When a user interacts with the React components (like adding a new note), it triggers an action. This action updates the store (the app’s central data) through a dispatcher. The store then emits a change event, causing the React components to update and re-render if necessary.
What are the key components of Flux architecture?
The Flux architecture consists of four main components: Actions, Dispatcher, Stores, and Views (React components). Actions are payloads of information that send data from the application to the dispatcher. The Dispatcher is a sort of central hub that manages all data flow in the application. Stores contain the application state and logic, and they are updated by the dispatcher. Finally, Views (React components) listen for changes in the stores and re-render themselves when those changes occur.
How can I manage the state of my note-taking app using Flux?
In Flux, the state of your application is typically stored in stores. When an action occurs (like adding a new note), it sends data to the dispatcher. The dispatcher then sends this data to the relevant store. The store updates its state and emits a change event. Any React components that are listening for changes in the store will then update and re-render themselves, reflecting the new state of the application.
How does Flux help in maintaining the scalability of the note-taking app?
Flux architecture helps maintain the scalability of the note-taking app by providing a clear and predictable data flow. This makes the app easier to understand and modify as it grows in complexity. The unidirectional data flow ensures that changes in one part of the app don’t unexpectedly affect other parts, reducing the likelihood of bugs and making the app easier to test and debug.
How can I handle user interactions in my note-taking app using React and Flux?
User interactions in a note-taking app built with React and Flux are typically handled through actions. When a user interacts with a React component (like clicking a button to add a new note), it triggers an action. This action sends data to the dispatcher, which updates the relevant store. The store then emits a change event, causing any listening React components to update and re-render.
How can I test my note-taking app built with React and Flux?
Testing a note-taking app built with React and Flux can be done using various testing libraries and frameworks. For unit testing React components, libraries like Jest or Enzyme can be used. For testing the Flux actions and stores, you can use Jest along with libraries like redux-mock-store or flux-mock-store. End-to-end testing can be done using tools like Cypress or Puppeteer.
How can I debug my note-taking app built with React and Flux?
Debugging a note-taking app built with React and Flux can be done using various tools. The React Developer Tools extension for Chrome and Firefox allows you to inspect the React component hierarchy, props, and state. For debugging Flux, you can log actions and state changes to the console, or use a tool like Redux DevTools if you’re using Redux as your Flux implementation.
Can I use other libraries or frameworks with React and Flux when building my note-taking app?
Yes, you can use other libraries or frameworks with React and Flux when building your note-taking app. For example, you might use a routing library like React Router for navigation, a testing library like Jest for testing, or a UI library like Material-UI for styling your app. The choice of libraries or frameworks will depend on your specific needs and preferences.
How can I optimize the performance of my note-taking app built with React and Flux?
Optimizing the performance of a note-taking app built with React and Flux can involve various strategies. One common strategy is to ensure that components only re-render when necessary, by carefully managing state and props and using React’s PureComponent or shouldComponentUpdate when appropriate. Other strategies might include optimizing the app’s initial load time by code splitting, or optimizing data fetching by using a library like Relay or Apollo.
![Sandeep Panda](https://uploads.sitepoint.com/wp-content/uploads/2014/01/DSC_0457-96x96.jpg)
Sandeep is the Co-Founder of Hashnode. He loves startups and web technologies.