Category Archives: React

Updating React Component state from props

There comes a time when the state we’re storing within our React Component needs to be updated, for example I have a ToggleButtonGroup which stores the state for the currently pressed/selected child item (i.e. you press a button in the group and the other’s deselect etc. basically a radio button).

When the component is created we might set our state from the properties like this

constructor(props) {
   super(props);

   this.state = { selected: this.props.selected }    
}

but the constructor is not called again, so what if our container component needs to change the state in our ToggleButtonGroup, so that when the properties are updated so that the ToggleButtonGroup reflects the property changes in the state.

Note: without some code in place to handle prop to state synchronisation will likely have state out of sync with expected values/behaviour.

We can use the componentDidUpdate function like this to update our state

componentDidUpdate(prevProps) {
   if(this.props.selected != prevProps.selected) {
      this.setState({selected: this.props.selected});
   }  
}

This occurs after the properties have been updated. See componentDidUpdate for more information.

You should have the guard to only change the state if the property actually changed.

componentDidUpdate is not called

The likely reason that the componentDidUpdate function is not called is if shouldComponentUpdate returns false.

getDerivedStateFromProps

An alternative to componentDidUpdate is to write a static function named getDerivedStateFromProps into your components/class. This is invoked before the render function for both initial and subsequent updates. I’ve not used this so best to read more at getDerivedStateFromProps.

Deprecated method

This method has been deprecated, but in case you come across it, we can use the component lifecycle function componentWillReceiveProps to achieve the same synchronisation of props to state. For example

componentWillReceiveProps(newProps) {
   if(newProps.selected != this.props.selected) {
      this.setState({selected: newProps.selected});
   }
}

PropTypes

PropTypes are used in React components to specify type information etc. If (as I am) you’re using TypeScript then for the most part such type extra information is not really required as we get a similar functionality via TypeScript interfaces. However, you’ll see these used a lot in JSX files where. not only can you define the expected types, but also the expected values.

To install

yarn add prop-types

PropTypes are only available in development mode which makes sense for the most part, so you would still need runtime checking on values supplied by the end user or the likes.

The usual way to write our propTypes in a JSX file is like this

import React from 'react';
import PropTypes from 'prop-types';

class Company extends React.Component {
    render() {
        return <div> 
        <h1>{this.props.name}</h1>
        {this.props.country ? <p>Country: {this.props.country}</p> : null}
        {this.props.size ? <p>Size: {this.props.size}</p> : null}
        </div>;   
    }
}

Company.propTypes = {
  name: PropTypes.string,
  country: PropTypes.string,
  size: PropTypes.oneOf(['small', 'medium', 'large']),
};

export default Company;

If we now change App.tsx to just have this

const App: React.FC = () => {
  return (
    <Company name="My Company" size="mega" />
  );
}

You’ll find this runs but an error log in your preferred browser debug tools will show that “maga” is not a valid option, because obviously it wasn’t oneOf the supplied options.

Let’s now change the Company.jsx file to a .tsx extension.

This will fail to transpile with an error such as “Type ‘{ name: string; size: string; }’ is not assignable to type ‘IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{}> & Readonly<{ children?: ReactNode; }>‘.”

Instead, we can use interfaces within TypeScript to handle the type checking at transpile time, hence this code gives us a similar set of function to the JSX version

type Size = 'small' | 'medium' | 'large';

interface CompanyProps {
  name?: string,
  country?: string,
  size?: Size
}

class Company extends React.Component<CompanyProps> {

    render() {
        return <div> 
        <h1>{this.props.name}</h1>
        {this.props.country ? <p>Country: {this.props.country}</p> : null}
        {this.props.size ? <p>Size: {this.props.size}</p> : null}
        </div>;   
    }
}

References

prop-types
ts-proptypes-transformer

Code editor for React

This is a quick post to say, if you’re looking for a JavaScript code editor UI for your application, the react-ace editor has so far proven itself very useful.

Install via yarn add react-ace and you’ll also want yarn add brace for themes, syntax colour.

Now import the dependencies like this

import AceEditor from "react-ace";
import "brace/mode/javascript";
import "brace/mode/python";
import "brace/theme/monokai";
import "brace/theme/github";

This includes two themes, monokai and GitHub for you to try along with two language colourings, in the form of javascript and python.

Now to use the editor, just drop the following code into your application

<AceEditor mode="python" //javascript
  theme="github" //monokai
  width="100%"
  onChange={this.handleOnChange}
  name="AceEditor"
  editorProps={{$blockScrolling: true}}
  value={code}
  fontSize={16}
/>          

In this example we’d have an onChange handler to store the changed text in from the editor, the value={code} is the source code that is initially displayed.

React and CSS

Standard approach to CSS

In a previous post we looked at an animated button and the issue(s) of having CSS in a separate .css file.

To recap, CSS can be implemented in a standalone .css file, for example AniButton.css might look like this (this is an abridged version of the CSS used in the previous post)

.button {
  background: #2B2D2F;
  height: 80px;
  width: 200px;
  text-align: center;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 0;
  right: 0;
  margin: 0 auto;
  cursor: pointer;
  border-radius: 4px;
}

This can then be imported into our React jsx/tsx file using

import "AniButton.css"

and everything works as per standard CSS. A couple of downsides to this are the CSS is in global namespace and also we’re using another language/DSL to define CSS whereas, it’d be nice if we could do all this in one language, i.e. in this case JavaScript/TypeScript.

Inline the style

From this global approach we can look towards inline of the CSS and also at the same time, removing the different language/DSL issue by declaring CSS in a JavaScript JSON style object, for example we remove the .CSS file and it’s import and then include the CSS style as an object, like this

const styles = {
  button: {
    background: "#2B2D2F",
    height: "80px",
    width: "200px",
    textAlign: "center",
    position: "absolute",
    top: "50%",
    transform: "translateY(-50%)",
    left: "0",
    right: "0",
    margin: "0 auto",
    cursor: "pointer",
    borderRadius: "4px"
  }
};

to apply this style we will need to add a style attribute to each of the elements we want a style applied to, for example

<div className="button" style={styles.button}>
</div>

One obvious downside of this inclusion of CSS along with our code is the separation of concerns might be seen as being lost, i.e. styling is now included along with the component code, however I tend to see React as lacking that separation of markup and code which you get with something like XAML/WPF, so I suppose it’s simply down to your interpretation of such things.

Styled components

Another solution which is a bit of a combination of CSS (syntax wise the names do not change to camelCase nor are values wrapped in quotes) and JavaScript is to use a library such as styled-components or emotion.

I’m going to look at Emotion’s implementation, so run the following

yarn add @emotion/styled @emotion/core

Now we can replace our button CSS and inline JavaScript CSS with style components, for example within your JSX/TSX file implement the following

import styled from '@emotion/styled'

const Button = styled.div`
  background: #2B2D2F;
  height: 80px;
  width: 200px;
  text-align: center;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 0;
  right: 0;
  margin: 0 auto;
  cursor: pointer;
  borderRadius: 4px;
`;

Now in usage our code looks like this

<Button className="button" onClick={this.handleOnClick}>
  <div className="text" style={styles.text} onClick={this.handleOnClick}>Submit</div>
</Button>

Converting animated button to React

I was looking at a nice little animation of a button on https://codepen.io/andrewmillen/pen/MoKLob and thought it’d be interesting turning it into a React component especially as I’m currently playing with animejs. As usual such things never “just work” but that also means it’s a good learning process.

So here goes, first off create your React application and add the animejs library…

  • yarn create react-app animatedbutton –typescript

From within the generated application run the following

  • yarn add animejs
  • yarn add @types/animejs

Note: I will not list all the code here, it’s available on GitHub, but I’ll list the changes and things of interest.

Next create a components folder off of the src folder and create AniButton.css, copying the CSS from the original link into that and then create AniButton.jsx which I’ve included here

Note: I used a jsx file as opposed to tsx simply because I didn’t want to spend too long fixing “type” issues initially, but when I did try this I found the ref is not expected on a path element, so we’ll stick with JSX for now.

export class AniButton extends React.Component {

  checkRef = React.createRef();

  constructor(props) {
    super(props);

    this.handleOnClick = this.handleOnClick.bind(this);
  }

  handleOnClick() {

    var pathEls = this.checkRef.current;
    var offset = anime.setDashoffset(pathEls);
    pathEls.setAttribute("stroke-dashoffset", offset);

    var basicTimeline = anime.timeline({
      autoplay: false
    })
    .add({
      targets: ".text",
      duration: 1,
      opacity: 0
    })
    .add({
      targets: ".button",
      duration: 1300,
      height: 10,
      width: 300,
      backgroundColor: "#2B2D2F",
      border: "0",
      borderRadius: 100
    })
    .add({
      targets: ".progress",
      duration: 2000,
      width: 300,
      easing: "linear"
    })
    .add({
      targets: ".button",
      width: 0,
      duration: 1
    })
    .add({
      targets: ".progress",
      width: 80,
      height: 80,
      delay: 500,
      duration: 750,
      borderRadius: 80,
      backgroundColor: "#71DFBE"
    })
    .add({
      targets: pathEls,
      strokeDashoffset: [offset, 0],
      opacity: 1,
      duration: 200,
      easing: "easeInOutSine"
    });

    basicTimeline.play();
  }

  render() {
    return (
      <main>
        <div className="button" onClick={this.handleOnClick}>
          <div className="text" onClick={this.handleOnClick}>Submit</div>
        </div>
        <div className="progress"></div>
        <svg x="0px" y="0px"
          viewBox="0 0 25 30">
          <path ref={this.checkRef} className="check st0" d="M2,19.2C5.9,23.6,9.4,28,9.4,28L23,2" />
        </svg>
      </main>
    )
  }
}

Note: A couple of changes I made to the CSS was change the progress bar name and fixed the background to gray. The reason I did this was simply because I had problems initially with the CSS displaying incorrectly and messed with things to try to resolve – I think the problem is unrelated to the changes I made though.

Also I seem to periodically lose the progress bar, not 100% why but just saying.

The key differences to the original source code are – obviously it’s coded into a React.Component. Event handlers are within the class, but really the main difference is the way we get access to the DOM path element via the ref={this.checkRef}. I’ve removed the loop from the original code which found the paths as we only have one here.

Next, the original code uses the Poppins font, so add the following the public/index.html

<link 
   href="https://fonts.googleapis.com/css?family=Poppins:600" 
   rel="stylesheet">    

The animejs code

At this point everything works nicely, so let’s look at the animejs code.

We need to get a reference to the DOM path object using

var pathEls = this.checkRef.current;
var offset = anime.setDashoffset(pathEls);
pathEls.setAttribute("stroke-dashoffset", offset);

The anime.setDashoffset code does the following

“Creates path drawing animation using the ‘stroke-dashoffset’ property. Set the path ‘dash-offset’ value with anime.setDashoffset() in a from to formatted value.”

Next we create an animejs, which basically allows us to coordinate each stage/step of the animation. Hence the first timeline is to immediately hides the .text using the opacity property and a very short duration.

Now we change the shape of the .button to a thinner button etc.

The next step is to display a progress overlay which starts from the centre (based upon the CSS) and expands out over it’s duration in a linear easing.

Once the previous step’s completed which reduce the .button to zero width to basically hide it and the .progress then changes colour and eventually turns into a circle.

Finally the check/tick is displayed using the strokeDashOffset, so basically this animates the drawing of the check.

CSS

For the most part the animejs timeline code is pretty simple and works a treat, so let’s take a look at the CSS side of things.

Firstly we can import the CSS using

import "./AniButton.css";

Which basically tells webpack (in a standard React application) to include the CSS in the bundle. Now the CSS can be applied to our elements in the usual ways, on an element type or via id or class (in React class becomes classname).

As stated previously though, if you require actual access into the DOM you’ll need to use something like a ref, but use sparingly.

CSS files like the ones we’re using are not really the preferred way to use CSS in React (see the React: CSS in your JS by Christopher Chedeau for some reasons why this is the case, admittedly it’s a few years old but still relevant now).

To save you watching the video, CSS files defines CSS globally which can make styling difficult to debug or understand at times. Also it’s equally difficult to know when CSS is redundant, i.e. an element s no longer used but we still maintain it’s CSS etc.

I’m not going to go into all the others ways to use CSS it this post, but obviously our best way to use CSS would be to have it used locally inlined with our component.

Hence we can instead convert our CSS to the following code within our AniButton.jsx file like this

const styles = {
  main: {
    height: "100vh",
    width: "100vw"
  },
  button: {
    background: "#2B2D2F",
    height: "80px",
    width: "200px",
    textAlign: "center",
    position: "absolute",
    top: "50%",
    transform: "translateY(-50%)",
    left: "0",
    right: "0",
    margin: "0 auto",
    cursor: "pointer",
    borderRadius: "4px"
  },
  text: {
    font: "bold 1.25rem/1 poppins",
    color: "#71DFBE",
    position: "absolute",
    top: "50%",
    transform: "translateY(-52%)",
    left: "0",
    right: "0"
  },
  progress: {
    position: "absolute",
    height: "10px",
    width: "0",
    right: "0",
    top: "50%",
    left: "50%",
    borderRadius: "200px",
    transform: "translateY(-50%) translateX(-50%)",
    background: "gray",
  },
  svg: {
    width: "30px",
    position: "absolute",
    top: "50%",
    transform: "translateY(-50%) translateX(-50%)",
    left: "50%",
    right: "0",
    enableBackground: "new 0 0 25 30"
  },
  check: {
    fill: "none",
    stroke: "#FFFFFF",
    strokeWidth: "3",
    strokeLinecap: "round",
    strokeLinejoin: "round",
    opacity: "0"
  }   
}

Note: underline or hyphens turn into camelCase names and values get wrapped in quotes.

Now we will need to explicitly add the styles to our render code using style={} syntax, so it looks like this

<main style={styles.main}>
  <div className="button" style={styles.button} 
       onClick={this.handleOnClick}>
     <div className="text" style={styles.text} 
       onClick={this.handleOnClick}>Submit</div>
  </div>
  <div className="progress" style={styles.progress}></div>
  <svg x="0px" y="0px"
    viewBox="0 0 25 30" style={styles.svg}>
    <path ref={this.checkRef} style={styles.check} 
      className="check st0" d="M2,19.2C5.9,23.6,9.4,28,9.4,28L23,2" />
  </svg>
</main>

We can now delete our AniButton.css file.

React and CSS

There are several ways to use CSS within React applications.

Inline CSS

We can create a structural type as a variable or const (more likely), for example

const style = {
  background: 'red',
  width: '100px',
  color: 'white'
};

export const MyStyledDiv: React.SFC<{}> = ({
}) => <div style={style}>Hello World</div>

In this case we’re using React.SFC just to reduce the code to a minimal, but fully fledged Components would work in the same way within the render function.

Style based components

With style based components we in essence create a component from the style using the back tick syntax along with the prefix styled.{element type}, so for example

const DivStyle = styled.div`
  background: red;
  width: 100px;
  color: white;
`;

export const MyStyledDiv: React.SFC<{}> = ({
}) => <DivStyle>Hello World</DivStyle>

CSS Modules

We can create separate files for our css (known as CSS Module StyleSheets).

In this case we create a .css file, for example MyStyledDiv.module.css which uses standard CSS syntax, so for example here my file

.myDivStyle {
    background-color: red;
    width: 100px;
    color: white;
}

Now in our component we import the CSS in much the same way as importing components, for example

import styles from './MyStyledDiv.module.css';

export const MyStyledDiv: React.SFC<{}> = ({
}) => <div className={styles.myDivStyle}>Hello World</div>

These style sheets are locally scoped to the component which imports them which helps us to reduce possible name clashes etc.

Standard CSS files

In this case we have standard CSS files, here we have a file named myStyleDiv.css with the following (which is the same as our module.css contents)

.myDivStyle {
    background-color: red;
    width: 100px;
    color: white;
}

Now we again import the CSS like this

import './MyStyledDiv.css';

export const MyStyledDiv: React.SFC<{}> = ({
}) => <div className="myDivStyle">Hello World</div>

Emotion

We can also use third party library’s such as emotion.

Here’s the same example using emotion

const DivStyle = styled.div(props => ({
  background: 'red',
  width: '100px',
  color: 'white'
}))

export const MyStyledDiv: React.SFC<{}> = ({
}) => <DivStyle>Hello World</DivStyle>

React pure component

I’m using the Airbnb style guide with eslint and one warning mentioned that a very simple Component subclass with no properties or state but it just has a render method, should probably be written as a pure component.

Below is an example of a pure component in it’s most simplistic and minimal form.

import React, { ReactElement } from 'react';

const MyButton = (): ReactElement => (
  <div>My Button</div>
);

export default MyButton;

React also comes with a PureComponent base class, hence we could write

class MyButton extends React.PureComponent {
   render() {
      <div>My Button</div>
   }
}

This is slightly different to our function version of a pure component in that it also handles properties and state, see the documentation for more information.

In both cases, these changes from a standard React Component result in performance increases, so very much worth baring in mind if your components are able to be written in these ways.

Getting started with Redux in React

Lot’s of Getting started posts at the moment, and probably a lot more to come. This one’s on using Redux with React and TypeScript.

Let’s create a sample application…

  • yarn create react-app {insert app name} –typescript
  • yarn add redux
  • yarn add react-redux
  • yarn add @types/react-redux
  • We can now delete the App.* files and the *.svg
  • Add folder components to src and also reducers
  • In src/components add Counter.tsx (we’re going to recreate the redux example component for in a tsx). Here’s the code
    import React, { Component } from 'react';
    
    interface CounterProps {
        value: number;
        onIncrement: () => void;
        onDecrement: () => void;
    }
    
    export default class Counter extends Component<CounterProps, {}> {
        constructor(props: CounterProps) {
            super(props);
            this.incrementAsync = this.incrementAsync.bind(this);
            this.incrementIfOdd = this.incrementIfOdd.bind(this);
        }
    
        incrementIfOdd() {
            if (this.props.value % 2 !== 0) {
              this.props.onIncrement()
            }
          }
        
        incrementAsync() {
          setTimeout(this.props.onIncrement, 1000)
        }
    
        render() {
            const { value, onIncrement, onDecrement } = this.props
            return (
              <p>
                Clicked: {value} times
                {' '}
                <button onClick={onIncrement}>
                  +
                </button>
                {' '}
                <button onClick={onDecrement}>
                  -
                </button>
                {' '}
                <button onClick={this.incrementIfOdd}>
                  Increment if odd
                </button>
                {' '}
                <button onClick={this.incrementAsync}>
                  Increment async
                </button>
              </p>
            );
        }
    }
    
  • Now in src/reducers add the file counterReducer.ts and put the following code into it
    export default (state: number = 0, action: any) => {
        switch (action.type) {
          case 'INCREMENT':
            return state + 1
          case 'DECREMENT':
            return state - 1
          default:
            return state
        }
    }
    
  • Finally, replace the contents of the index.tsx with the following
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import * as serviceWorker from './serviceWorker';
    import { Provider } from 'react-redux'
    import { createStore } from 'redux'
    import counter from './reducers';
    import Counter from './components/Counter';
    
    const store = createStore(counter);
    
    const render = () => {
        ReactDOM.render(
            <Provider store={store}>
                <Counter
                    value={store.getState() as number}
                    onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
                    onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
                />,
            </Provider>, 
            document.getElementById('root'));
    }
    
    render();
    store.subscribe(render);
    
    serviceWorker.unregister();
    
  • If everything is correct you should be able to execute yarn start and see the web page with +, – buttons etc. and clicking + or – will increment and decrement the displayed counter.

That’s a lot to take in, so what have we actually done?

Redux supplied a storage container for our application state. In the index.tsx file we create a store passing our reducer (src/reducers/counterReducer.ts) Reducers are called in response to the store.dispatch calls. In this case within index.tsx we dispatch the actions with the type set to strings INCREMENT and DECREMENT. The reducer receives these messages then makes state changes (although not actually changing the state directly but returning a new state).

As the Redux documentation on reducers states, “Remember that actions only describe what happened, but don’t describe how the application’s state changes”.

The next piece of code to look at is

store.subscribe(render);

We’ve wrapped the render code for the application in the render function. Subscribe then allows us to listen to messages on the redux store and calls the render function to render changes. The following part of the Counter code within the index.tsx file then simply assigns the current store state to the Counter value property

value={store.getState() as number

There’s not lot to really say about the Counter component which is standard React code for displaying buttons etc. and exposing properties as used in index.tsx.

More…

This is a simplistic example. Redux documentation states that it’s not advisable to write the store.subscribe code but instead use the connect function provided by React Redux for this functionality.

In this instance we create a src/containers folder and add the file CounterContainer.ts, here’s the code

import {  connect } from 'react-redux'
import Counter from './../components/Counter';

const mapStateToProps = (state: any) => {
    return {
        value: state as number
    }
}

const mapDispatchToProps = (dispatch: any) => {
    return {
        onIncrement: () => dispatch({ type: 'INCREMENT' }),
        onDecrement: () => dispatch({ type: 'DECREMENT' }),
    }
}

export const CounterLink = connect(
    mapStateToProps,
    mapDispatchToProps
)(Counter);

export default CounterLink;

Here we write code to map state to properties and dispatch to properties (if we don’t handle either of these we simply supply a null in place of the function in the connect function.

As you can probably see, in mapStateToProps, the state is supplied and we simply apply it to the value property (which is on our Counter). In mapDispatchToProps we link the onIncrement and onDecrement to the dispatch functions.

Finally in index.tsx replace the Counter component with our newly created and exported CounterLink, i.e. here the new index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import counterReducer from './reducers/counterReducer';
import CounterLink from './containers/CounterContainer';

const store = createStore(counterReducer);

ReactDOM.render(
    <Provider store={store}>
        <CounterLink />
    </Provider>, 
    document.getElementById('root')
);

serviceWorker.unregister();

Getting started with Storybook

Storybook allows us to prototype and test UI components in isolation from your application. We’re going to “get started” using Storybook with React, but it also supports Vue and Angular.

Let’s create the project and add required libraries.

Note, that the npx command should not be run from VSCode (or other Atom based editor by the sounds of it), I got an error EPERM: operation not permitted, unlink.

In the steps below we also install enzyme for test rendering. We’ll also add material-ui, just for the fun of it.

  • npx -p @storybook/cli sb init
  • yarn create react-app storybooksample –typescript
  • yarn add @types/storybook__react -D
  • yarn add @types/enzyme -D
  • yarn add enzyme -D
  • yarn add enzyme-adapter-react-16 -D
  • yarn add @types/enzyme-adapter-react-16 -D
  • yarn add @material-ui/core

The storybook “getting started” web page suggests we now run the following commands to check everything was installed correctly, so let’s do this, run

  • yarn test
  • yarn storybook

Let’s now remove the storybook generated code as we’ll replace this with our own code. So in the folder src/stories, delete index.js and add a file named setupTests.ts to the src folder, here’s the code for this file

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

Before we go too much further let’s create a sample component that will use to demonstrate testing via storybook. In the src folder add a folder named components/LogButton and in this create a file named LogButton.tsx – the name’s unimportant ofcourse, this is just a sample component which will be a new Button component, but hopefully will help demonstrate how we can use storybook.

In LogButton.tsx place the following code

import React from 'react';
import Button from '@material-ui/core/Button';

interface LogButtonProps {
    onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
}

export class LogButton extends React.Component<LogButtonProps, {}>{    
   render() {
      return (
         <div>
            <Button variant='contained' onClick={this.props.onClick}>Log</Button>
         </div>
      );
   }
}

Let’s now create a standard unit test for this component, this is not required for storyboard, but it’s good practise. In the same src/components/LogButton folder add the file LogButton.test.tsx and here’s a very simple test to got into this file

import React from 'react';
import { mount } from "enzyme";
import { LogButton } from "./LogButton";

test('Loading component should not throw error', () => {
    const logButton = mount(<LogButton />);
    expect(logButton.exists()).toBe(true);
});

This will just check that the component, when loaded, loads successfully. We can now run yarn test to verify this code works.

Now let’s get storybook up and running with our component.

In the .storybook folder, replace the config.js code with the following

import { configure } from '@storybook/react';

const req = require.context('../src', true, /.stories.(ts|tsx|js)$/)
const loadStories = () => req.keys().forEach(filename => req(filename));
configure(loadStories, module)

This will automatically load stories based upon the filename/extensions .stories.

Alongside LogButton.tsx and LogButton.test.tsx, add LogButton.stories.tsx. This will supply the code required to integrate into storybook and also allow us to write code to allow us to test the UI component via storybook.

import React from 'react';
import { storiesOf } from '@storybook/react';
import { LogButton } from './LogButton';
import { action } from '@storybook/addon-actions';

storiesOf('LogButton', module)
    .add("Log", () => <LogButton onClick={action('clicked')} />);

Now run yarn storybook and the storybook server and UI should display along with our LogButton now being the only component available. Selecting the “Log” story, when the LogButton is clicked the action will display the text “clicked”.

So what we’ve done is create a very simply component based upon a Button which we also declared some properties – well in this case we’ve created a single property, an onClick event. The story created for the button then hooks into the button’s click event and when used via storybook allows us to test the control in isolation via storybook’s UI. This is not a unit test or automated test so much as an interactive UI test where, as developers, we can verify our functionality. Let’s say the Button’s text changed when clicked, now we could start interacting with the button via storybook and confirm everything works as expected.

There’s a lot more to storyboard than the above, but this is a good starting point.

Creating React components in TypeScript

By default we’ll usually create a React component (in a .tsx file) using a React.Component base class, for example

export default class GridSampleComponent extends Component<{}, {}> {
   render() {
      return (
         <div></div>
      );
   }
}

But as per my post here we can also create components through functions, which ofcourse makes sense when you see a .jsx file which tend to default to using functions.

Hence we can create the following

const MyButton = (props: any) => {
   return 
      <Button variant="contained" 
         color="secondary">{props.children}</Button>;
};

In the code above we’ve basically created a new component from the function which takes properties which are implicitly passed to the function via React, i.e. the code for using the above might look like this

<MyButton>Hello</MyButton>

The child elements are passed through to our function. In this case it’s just the string “Hello”.