Home >Web Front-end >JS Tutorial >Creating a Note Taking App with React and Flux
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.
Flux relies on unidirectional data flow. We have two key components in the Flux pattern:
To reinforce the concept let’s take a real world example. For example, in a note making app you can have the following arrangement:
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.
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:
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:
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:
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:
grunt watch grunt nodemon
Reflux.createActions is used to create Actions. We export these Actions in order to use them in our components.
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) :
<span>var Reflux = require('reflux'); </span> <span>var NoteActions = Reflux.createActions([ </span> <span>'createNote', </span> <span>'editNote' </span><span>]); </span> module<span>.exports = NoteActions;</span>
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.
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:
<span>var Reflux = require('reflux'); </span><span>var NoteActions = require('../actions/NoteActions'); </span> <span>var _notes = []; //This is private notes array </span> <span>var NoteStore = Reflux.createStore({ </span> <span>init: function() { </span> <span>// Here we listen to actions and register callbacks </span> <span>this.listenTo(NoteActions.createNote, this.onCreate); </span> <span>this.listenTo(NoteActions.editNote, this.onEdit); </span> <span>}, </span> <span>onCreate: function(note) { </span> _notes<span>.push(note); //create a new note </span> <span>// Trigger an event once done so that our components can update. Also pass the modified list of notes. </span> <span>this.trigger(_notes); </span> <span>}, </span> <span>onEdit: function(note) { </span> <span>// Update the particular note item with new text. </span> <span>for (var i = 0; i < _notes.length; i++) { </span> <span>if(_notes[i]._id === note._id) { </span> _notes<span>[i].text = note.text; </span> <span>this.trigger(_notes); </span> <span>break; </span> <span>} </span> <span>} </span> <span>}, </span> <span>//getter for notes </span> <span>getNotes: function() { </span> <span>return _notes; </span> <span>}, </span> <span>//getter for finding a single note by id </span> <span>getNote: function(id) { </span> <span>for (var i = 0; i < _notes.length; i++) { </span> <span>if(_notes[i]._id === id) { </span> <span>return _notes[i]; </span> <span>} </span> <span>} </span> <span>} </span><span>}); </span> module<span>.exports = NoteStore; //Finally, export the Store</span>
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:
<span>var React = require('react'); </span><span>var NoteList = require('./NoteList.jsx'); </span><span>var NoteStore = require('../../stores/NoteStore'); </span> <span>var NoteListBox = React.createClass({ </span> <span>getInitialState: function() { </span> <span>return { notes: NoteStore.getNotes() }; </span> <span>}, </span> <span>onChange: function(notes) { </span> <span>this.setState({ </span> <span>notes: notes </span> <span>}); </span> <span>}, </span> <span>componentDidMount: function() { </span> <span>this.unsubscribe = NoteStore.listen(this.onChange); </span> <span>}, </span> <span>componentWillUnmount: function() { </span> <span>this.unsubscribe(); </span> <span>}, </span> <span>render: function() { </span> <span>return ( </span> <span><div className="col-md-4"> </span> <span><div className="centered"><a href="" onClick={this.onAdd}>Add New</a></div> </span> <span><NoteList ref="noteList" notes={this.state.notes} onEdit={this.props.onEdit} /> </span> <span></div> </span> <span>); </span> <span>} </span><span>}); </span> module<span>.exports = NoteListBox;</span>
So, whenever there is a change onChange method of the component is called. This method receives an updated note list and changes the state.
<span>this.unsubscribe = NoteStore.listen(this.onChange);</span>
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:
grunt watch grunt nodemon
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.
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!
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
The above is the detailed content of Creating a Note Taking App with React and Flux. For more information, please follow other related articles on the PHP Chinese website!