Marvelous Thorough React Tutorial/Workshop — part 8

Wojciech Bilicki
10 min readSep 2, 2017

--

This series of articles is written as notes used during the workshop hosted in Gdańsk, Poland by OKE Software Poland. It does not pose as comprehensive and complete tutorial serie for anyone, yet it will be used for workshop aimed for developers that have none experience building SPA using React. I am posting it here as I think some people might find it useful. Please do not hesitate to correct me, if you find some nonsense. Also please bear in mind that english isn’t my native language, so forgive me any grammar and linguistic mistakes. Enjoy!

BTW here’s previous part:

Plan

Part 8— React Storybook, Redux introduction.

We’re pretty much finished with React on its own. I hope you see it now as a simple and extensible solution for creating modern web apps. There’s just one cool tool left that I’d like to show you that let’s you work on components in isolation.

As I said many times before YOU MIGHT NOT NEED REDUX. Except for the times that you really need and it can help you with managing the data flow in your application (not without some overhead of course).

React Storybook

Your application will grow. It’s natural in development of any product. You will have your components, smart components, HOC components. Your api connection layer, your styles, styled components, configurations files. There’s just so many things that have to be connected together to get that page render to be successful! Some of them are just noise. You just want to work on how your hero card is looking or how your calendar behaves. Or how your list of items looks when you pass an empty array to it. You’d like to work on one particular state of your component and don’t care about everything around. Behold React Storybook:

https://storybook.js.org

Dude, I tell you. It’s soo good. It gives you an isolated environment for working on your components. Also can be used for testing with Enzyme as well as be treated as some kind of documentation. You don’t know how given component is working? Just run your Storybook. This is a little bit offtopic from our workshop, but let’s give it a shot:

npm i -g @storybook/cli or yarn global add @storybook/cli
getstorybook

and than in your just type: yarn run storybook and go to url shown in your terminal (for me that is: http://localhost:6006/ ) and you can see the storybook from your app. Few things were added automatically to our app that is .storybook folder where we can find the webpack.config.js as well as the config.js for storybook and stories folder with examples of how to use Storybook to load files.

Ok let’s try adding one of our own stories. To make it run we must add one thing to our .babelrc file:

"presets": [
"es2015",
...
]

and run yarn add --dev babel-preset-es2015 .

Now we can run yarn run storybook and see our Storybook dashboard. It comes with one component added from the very beginning just to show you what’s what. Let’s add one of ours, shall we?

Go to .storybook/config and in loadStories function add a line:

require('../js/HeroCard.stories.jsx');

Then of course we have to create this file :):

Ok so our first try would be something like this:

https://gist.github.com/wojciech-bilicki/7bd73a59cce2fda16d67179208ff0300

We create simple hero object to pass for our component in a story. Yet if we go to our storybook, we’d get:

Yeah, fantastic. So as I said storybook is isolated environment and it has its pros and cons. This is obviously a problem. Let’s try fixing it. We need to inform our storybook that we are using router and link element. Adding storybook-router to project should fix this:

yarn add --dev storybook-router

And than we can change our HeroCard.stories.jsx:

And than we’ll see our component working. You can see that the font is different. That’s because we didn’t specify global font for our storybook. I know it doesn’t seem like much but using isolated env for developing your component and its possible states is quite powerful. For example we could implement loading state for our component and display a spinner showing when the loading={true} prop is passed to our HeroCard or what to display when to image is missing? It also allows you to do testing. I would even suggest to start working with storybook first and build your component library and than link them in a real app.

Redux

Whoa, that’s like another big part of our workshop. Let’s try answering few simple questions first. What is Redux? Imagine taking everysingle piece of data and state that lives inside your component and moving it out of it.

This is the final answer to the question: ‘Where does your web app data lives?”. You can literally show it to someone in a concise and readable form using Redux Dev Tools (will add those in a sec). Every single piece of data that you need to render your application can be moved there (just consider whether it should be).

Redux can seem pointless and hard at the beginning, but is actually pretty elegant, simple (because functional) piece of art. You can view the Redux store (store is where you put all your data) as a JSON document or a tree.

There are few very important rules that comes with using Redux. The most imporant ones are:

  • You can’t modify state of Redux directly (same as with components where we had to use setState) you have to dispatch an Action.
  • Actions are pure functions (working only on the passed params, not creating any variables, returning the value) that have to have type key specified as constant
  • Reducers are responsible for taking an action and describing how the Redux state is being modified.

If you connect all Redux parts and components you get Unidirectional Data Flow:

Source: https://www.smashingmagazine.com/2016/06/an-introduction-to-redux/

If you look into the diagram above, you’ll see that our data and information are flowing always in one direction with predictable order:

  1. It all starts with an event in one of your components i.e. clicking submit button in form.
  2. Than the action creator dispatches a function.
  3. This function is matched against current state of Redux store by reducer. This ends up building completely new state (copy of previous with reducer function applied).
  4. This updated new state is provided to all interested (connected) components.
  5. Components take new state and are rerendered. We stop here.

It’s rather simple as I said, but gives us many fantastic features i.e.:

  • Very easy to test,
  • Easy to implement undo/redo
  • Time travelling debugging
  • Basic cache
  • Sharing state across components is easy:
https://www.foreach.be/why-react-redux-combo-magic

But with great power, comes great responsibility™️. I’m not really sure whether you should rush and add the Redux to the app from the very start. If the app architecture and user flow are rather simple I will opt against it.

Ok so let’s write our first reducer. It will handle the change of our searchTerm it TopBar.jsx. Since I told many, many times that’s Redux is highly testable let’s try a bit of TDD approach, shall we?

The file structure that we have isn’t the one I’d be using for the real project, but just to keep a bit of order let’s add a redux folder in our js folder and in our __tests__ folder let’s add reducers.spec.js and let’s try to specify what do we actually expect from our reducer. We’ll go really slow with that one:

Ok so we expect our rootReducer to have just one part of state, searchTerm that is. By default it should return just empty string. If we run our test now it will fail naturally. Let’s fix it with minimal amount of code that we can:

Ok that’s great! And useless. As I told you before we want to move our components state to redux. The equivalent of setState is dispatching an action, so we need to write an actionCreator, let’s start with test again. Actions are pure function returning Flux Standard Actions (FSA) (basically and object with type (being string constant) and payload representing an intent of what user would like to do). Let’s create actionCreateor.spec.js:

That’s pretty straight forward. We call our action and expect it to return object with FSA props.

Ok so our actionCreators.js is rather simple, too:

Now our test passed, but you may have come to conclusion that something is wrong with our setSearchTerm function. Yup, the payload is hardcoded. Why? Because we always write the minimal amount of code needed for our tests to pass. In this case we naturally add more tests to make sure that our actionCreator behaves the way it supposed to:

Great now we have a failing test again. Let’s update our actionCreator:

const setSearchTerm = searchTerm => ({   type: SET_SEARCH_TERM,   payload: searchTerm});

Fantastic! Now we can double back to our reducers! Now if our reducer recieves an action of given type, we’d like it to create new state, reflecting the changes that we requested. Let’s update reducers.spec.js:

Nice we have a failing test again. We also had to pass some kind of action to our first test as it is mandatory for reducer to take in action. They are the only way to modify the redux state. Let’s make it pass. Update our reducer:

Ok now it’s taking some shape. We have default state defined as constant. Our rootReducers is taking as params the action as well as default/previous state. We also use the spread operator to provide fresh copy of updated state. We could use Object.assign, but I find it a little to verbose. It’s just a matter of prefernce, really. To use spread operator we must add a babel plugin:

yarn add --dev babel-plugin-transform-object-rest-spread

and update our .babelrc file:

"plugins": [  "react-hot-loader/babel",  "transform-class-properties",   "transform-object-rest-spread"],

Ok so the TDD cycle as you probably know is called red, green, refactor. You write failing test, then you make it pass then you check whether there’s something that we could refactor. I’m going to change just one bit in our stor e.js code:

https://gist.github.com/wojciech-bilicki/79bb3db7d2244c4c5e1b61ed7d0b0f95

Not to much, but it will be helpful a bit later on.

Now we need to actually connect react to redux. This is done in two steps. First step is adding global Provider component to our app to actually create the store. Second part is connecting each component that needs to have access to a store. Let’s start by adding the packages that we need:

yarn add redux react-redux

Ok fantastic! Now we can actually connect our components to the store.

We do this by using connect function (technically HOC) from react-redux. It takes two function as params. First is the function describing how parts of redux state should be mapped to component props ( mapStateToProps). Second function maps actions in our application to the props that later can be fired on i.e. button click ( mapDispatchToProps):

What’s great is that we could move back to functional component as our state resides now in redux. Please be noted that the mapDispatchToProps and mapStateToProps names are just a convention, but the order of passing those to connect naturally matters.

Ok now let’s try to make our Redux just a little bit more ‘flowy’. Let’s create actions.js file:

// @flow/const SET_SEARCH_TERM = 'SET_SEARCH_TERM';export { SET_SEARCH_TERM };export type ActionType = 'SET_SEARCH_TERM';export type Action = {   type: ActionType,   payload: string};

Ok this is actually providing types of actions that are possible in our app. It’s just one for now. Let’s head to actionCreators.js:

Ok now we must type our store. I would also rather see it refactored to seperate pieces of state using combineReducers function:

combineReducers function creates the root reducer automatically. What’s even better is that each reducer provided to this function now is completely seperate. Before that each reducer would get the copy of the entire state. It may cause some unexpected results, like reducers overriding the part of the state that it shouldn’t know about in the first place. Separation provided by combineReducers pushes only the parts of the state that the reducer should be interested in for new copy creation.

Great, one last thing that I’d like to present in this part is redux Devtools. Fire up Chrome or Firefox and install redux DevTools add on. To make it work with our app we need to add a bit of configuration.

Now if we run our app in Chrome browser and fire up the Redux tab in developer tools we will discover whole new world! I especially love the Chart window which shows the graphical representation of what redux actually is in our app. Also go ahead and check out LogMonitor and try typing something into search input. You can see everything step by step.

Also check out the free reducer test in inspector panel. You can export the state of your store to json file any time. This is extremely powerful debugging tool that you’ll use a lot, I promise.

Ok that’s it for now. We’ll go into async redux in the next part. Cheers.

--

--

Wojciech Bilicki

Mobile & FrontEnd Developer at OKE Software Poland. Marvel fan. Can't live without music and books.