<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Carl Topham</title>
    <description>The latest articles on DEV Community by Carl Topham (@designer023).</description>
    <link>https://dev.to/designer023</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F567623%2Ff9222679-b5b7-43bf-856b-ed9a3be17d32.jpeg</url>
      <title>DEV Community: Carl Topham</title>
      <link>https://dev.to/designer023</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/designer023"/>
    <language>en</language>
    <item>
      <title>Putting the `S` in SOLID JavaScript</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Tue, 16 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/putting-the-s-in-solid-javascript-l82</link>
      <guid>https://dev.to/designer023/putting-the-s-in-solid-javascript-l82</guid>
      <description>&lt;p&gt;&lt;code&gt;S&lt;/code&gt; - Single-Responsibility Principle (SRP) is the first design principal covered by &lt;code&gt;SOLID&lt;/code&gt; principals.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SOLID is an Object oriented design principal. It stands for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;S&lt;/code&gt; - Single-responsiblity Principle&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;O&lt;/code&gt; - Open-closed Principle&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;L&lt;/code&gt; - Liskov Substitution Principle&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;I&lt;/code&gt; - Interface Segregation Principle&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;D&lt;/code&gt; - Dependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The principals were originally created and promoted by Robert Martin (aka &lt;a href="http://en.wikipedia.org/wiki/Robert_Cecil_Martin"&gt;Uncle Bob&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;SRP simply states that Classes and functions should only have a single responsibility. This sounds fairly easy to do in principle, but in practice, especially in React it can be slightly more nuanced. Let me explain with a class that handles an Artist and the artist's details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Wrong!
class Artist {
    constructor(id) {
        this.id = id;
        this.getBio()
        this.getSingles();
        this.getAlbums()
    }

    getBio() {
        fetch(`/artist/${this.id}/bio/`)
       //... do stuff with data
    }

    getSingles() {
        fetch(`/artist/${this.id}/singles/`)
        //... do stuff with data
    }

    getAlbums() {
        fetch(`/artist/${this.id}/articles/`)
        //... do stuff with data
    }
}

const theArtist = new Artist('formerlyKnownAsPrince');

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example the &lt;code&gt;Artist&lt;/code&gt; class is responsible for multiple things. It should just represent an artist, but it also has the responsibility to get the bio, the singles and the albums.&lt;/p&gt;

&lt;p&gt;Why is this bad? For example, if we ever need to update &lt;code&gt;getBio&lt;/code&gt; to have an extra property, such as &lt;code&gt;this.fetchedData = true&lt;/code&gt; we could end up breaking other parts of the class. We could be reeeeeally careful and maybe use more specific variables like &lt;code&gt;this.fetchedBio&lt;/code&gt; but the more complex our application and classes/functions get the harder it will be to manage and track this. Instead, we can split the responsibilities up and create different classes and funcions that only handle one responsibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Right!

function getBio(id) {
    fetch(`/artist/${id}/bio/`)
    //... do stuff with data
    return bio;
}

function getSingles(id) {
    fetch(`/artist/${id}/singles/`)
    //... do stuff with data
    return singles;
}

function getAlbums(id) {
    fetch(`/artist/${this.id}/articles/`)
    //... do stuff with data
    return albums;
}

class Artist {
    constructor(bio, singles, albums) {
        this.bio = bio;
        this.singles = singles;
        this.albums = albums;
    }
}

function getArtistDetails(id) {
    const bio = getBio(id);
    const singles = getSingles(id);
    const albums = getAlbums(id);

    return { bio, singles, albums };
}

const { bio, singles, albums } = getArtistDetails('formerlyKnownAsPrince')

const theArtist = new Artist(bio, singles, albums);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have split up the Artist class and moved the methods to pure functions. Each one has a single responsibility.&lt;/p&gt;

&lt;p&gt;But what about the &lt;code&gt;getArtistDetails&lt;/code&gt; function being responsible for getting the 3 things (bio, singles and albums)? Well this is ok because that it's really 1 responsibility - to get the data!&lt;/p&gt;

&lt;p&gt;Now if we want to save that artist, then we can create a new class - &lt;code&gt;Saver&lt;/code&gt; which can have just one responsibility too! We don't have to worry about what other methods are on the Artist or what properties it may have. We can just take an &lt;code&gt;Artist&lt;/code&gt; and save it without worrying about breaking other properties or methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//... the rest
const theArtist = new Artist(bio, singles, albums); // from before

class Saver {
    save (data) {
        // something to save the data
    }
}

const saver = new Saver();

saver.save(theArtist);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;The same principles apply to react, whether you are creating &lt;code&gt;component classes&lt;/code&gt; or &lt;code&gt;functional components&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Wrong
const Artist = ({ id }) =&amp;gt; {
    const [bio, setBio] = useState(null);
    const [singles, setSingles] = useState([]);
    const [albums, setAlbums] = useState([]);

    useEffect(()=&amp;gt; {
        fetch(`/artist/${id}/bio/`)
            .then(data =&amp;gt; {
                setBio(data);
            })
    }, [id])

    useEffect(()=&amp;gt; {
        fetch(`/artist/${id}/singles/`)
            .then(data =&amp;gt; {
                setSingles(data);
            })
    }, [id])

    useEffect(()=&amp;gt; {
        fetch(`/artist/${id}/albums/`)
            .then(data =&amp;gt; {
                setAlbums(data);
            })
    }, [id])

    const singlesList = singles.map(single =&amp;gt; (
        &amp;lt;li key={single.id}&amp;gt;{single.title} - {single.releaseDate}&amp;lt;/li&amp;gt;
    ));

    const albumsList = albums.map(album =&amp;gt; (
        &amp;lt;li key={album.id}&amp;gt;{album.title} &amp;lt;img src={album.artWorkUrl} /&amp;gt;&amp;lt;/li&amp;gt;
    ));

    return (
        &amp;lt;div&amp;gt;
            { bio ? &amp;lt;h2&amp;gt;{bio.name}&amp;lt;/h2&amp;gt; : null }
            &amp;lt;ul&amp;gt;{singlesList}&amp;lt;/ul&amp;gt;
            &amp;lt;ul&amp;gt;{albumsList}&amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a few things going on that are wrong with the component. There is data retrieval/state logic and there is presentational logic all in the same component. We have the rendering of each list withing the main component. It makes the component very hard to reuse, or change for other uses because it's doing so many things.&lt;/p&gt;

&lt;p&gt;Let's fix it! We will separate the data logic from the presentational UI and logic. Each part of the UI has it's own dedicated single responsibility component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const Bio = ({bio}) =&amp;gt; {
    if (!bio) return null;

    return &amp;lt;h2&amp;gt;{ bio.name}&amp;lt;/h2&amp;gt;;
}

const SingleList = ({singles}) =&amp;gt; {
    const singlesList = singles.map(single =&amp;gt; (
        &amp;lt;li key={single.id}&amp;gt;{single.title} - {single.releaseDate}&amp;lt;/li&amp;gt;
    ));

    return (
        &amp;lt;ul&amp;gt;
            {singlesList}
        &amp;lt;/ul&amp;gt;
    )
}

const AlbumList = ({albums}) =&amp;gt; {
    const albumsList = albums.map(album =&amp;gt; (
        &amp;lt;li key={album.id}&amp;gt;{album.title} &amp;lt;img src={album.artWorkUrl} /&amp;gt;&amp;lt;/li&amp;gt;
    ));

    return (
        &amp;lt;ul&amp;gt;
            {albumsList}
        &amp;lt;/ul&amp;gt;
    )
}

const ArtistCard = ({bio, singles, albums}) =&amp;gt; (
    &amp;lt;div&amp;gt;
        &amp;lt;Bio bio={bio} /&amp;gt;
        &amp;lt;SingleList singles={singles} /&amp;gt;
        &amp;lt;AlbumList albums={albums} /&amp;gt;
    &amp;lt;/div&amp;gt;
)

const Artist = ({ id }) =&amp;gt; {
    const [bio, setBio] = useState(null);
    const [singles, setSingles] = useState([]);
    const [albums, setAlbums] = useState([]);

    useEffect(()=&amp;gt; {
        fetch(`/artist/${id}/bio/`)
            .then(data =&amp;gt; {
                setBio(data);
            })
    }, [id])

    useEffect(()=&amp;gt; {
        fetch(`/artist/${id}/singles/`)
            .then(data =&amp;gt; {
                setSingles(data);
            })
    }, [id])

    useEffect(()=&amp;gt; {
        fetch(`/artist/${id}/albums/`)
            .then(data =&amp;gt; {
                setAlbums(data);
            })
    }, [id])

    return &amp;lt;ArtistCard bio={bio} singles={singles} albums={albums} /&amp;gt;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it means that we can reuse the UI in other places, for example we could use the &lt;code&gt;ArtistCard&lt;/code&gt; for a list of artists. We didn't need to recreate the same UI or handle any logic. It just works™️.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const ArtistList = ({listOfArtists}) =&amp;gt; {
    const artists = listOfArtists.map(({bio, singles, albums}) =&amp;gt; (
        &amp;lt;ArtistCard bio={bio} singles={singles} albums={albums} /&amp;gt;
    ));

    return &amp;lt;div&amp;gt;{artists}&amp;lt;/div&amp;gt;;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the components we have created are all pure (given the same input they will return the same value) this means that we gain 2 very useful benefits.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can optimise them easily with react &lt;code&gt;memo&lt;/code&gt; since the same input returns the same output.&lt;/li&gt;
&lt;li&gt;Testing is easy because, again, the output is always the same given the same input, and we don't need to depend on the data source, so we can supply our own data any way we want to mock it!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Single-Responsibility Principle is a great fundamental principle to learn and use because it makes your Classes, functions and components more reliable, reusable, optimisable and testable.&lt;/p&gt;

&lt;p&gt;Just remember to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check every function you create to see if there are any smaller parts that can be moved in to single use functions. 2.Check your functions to make sure they are not doing 2 or more different things.&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>React and Material UI (MUI)</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Fri, 08 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/react-and-material-ui-mui-2793</link>
      <guid>https://dev.to/designer023/react-and-material-ui-mui-2793</guid>
      <description>&lt;p&gt;When I first used Material UI, I struggled with a few concepts. I've written this quick overview to help get my head around it, and hopefully guide others to a quicker start.&lt;/p&gt;

&lt;p&gt;I'm going to make a new project and then create some (horrible) styles!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the project and add MUI
&lt;/h2&gt;

&lt;p&gt;Create a project and install the Material UI package&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app material-ui-playground


cd material-ui-playground


yarn add @material-ui/core


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Clean up the initial templates
&lt;/h2&gt;

&lt;p&gt;Remove some unused contents (import of the styles) from the &lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import ReactDOM from 'react-dom';
// import './index.css'; DELETE this line and the file
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById('root')
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the &lt;code&gt;App.js&lt;/code&gt; content with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const App = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Material UI Playground&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a theme
&lt;/h2&gt;

&lt;p&gt;First we need to define a &lt;a href="https://material.io/resources/color/#!"&gt;palette&lt;/a&gt; to work with - This will define the core &lt;code&gt;color&lt;/code&gt; and tones of the UI.&lt;/p&gt;

&lt;p&gt;Then we can use the palette to &lt;a href="https://material-ui.com/customization/palette/"&gt;create a theme&lt;/a&gt; that we can use around the site. It's not just the &lt;code&gt;color&lt;/code&gt; that can be in a theme, but also all other properties such as &lt;code&gt;spacing&lt;/code&gt; and &lt;code&gt;font-family&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my example we will be using colors only. You can import the core colors from the &lt;code&gt;mui&lt;/code&gt; package and use them. They come with a range of shades built. We can also specify &lt;code&gt;light&lt;/code&gt;, &lt;code&gt;dark&lt;/code&gt; and &lt;code&gt;contrastText&lt;/code&gt; colors for our palette, but if we don't MUI will automagically do this for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createMuiTheme } from '@material-ui/core/styles';

import { purple } from '@material-ui/core/colors';
import { amber } from '@material-ui/core/colors';
import { red } from '@material-ui/core/colors';

const theme = createMuiTheme({
    palette: {
        primary: {
            main: purple["500"],
            // We don't need to set the text colour as MUI will work this out
        },
        secondary: {
            main: amber["600"],
            // We will manually set the alternate colours
            light: amber["200"],
            dark: amber["900"],
            contrastText: '#222222'
        },
        error: {
            main: red["500"]
        },
        background: {
            default: '#fff',
        },
    },
});

export default theme;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the &lt;code&gt;App.js&lt;/code&gt; import the theme and theme provdier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThemeProvider } from '@material-ui/styles';

import theme from "./theme";

// ... the rest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then wrap the returned DOM in a &lt;code&gt;&amp;lt;ThemeProvider /&amp;gt;&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ... rest

const App = () =&amp;gt; {
  return (
      &amp;lt;ThemeProvider theme={theme} &amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Material UI Playground&amp;lt;/h1&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/ThemeProvider&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the components within the &lt;code&gt;ThemeProvider&lt;/code&gt; will all be able to use the theme we have just created. Let's use it.&lt;/p&gt;

&lt;p&gt;We could just import the components directly into our &lt;code&gt;App&lt;/code&gt; but to keep them nice and tidy (and reusable elsewhere) we will make a component!&lt;/p&gt;

&lt;p&gt;Create a component &lt;code&gt;components/TitleBlock.index.jsx&lt;/code&gt;. In this we will use the build in MUI Typography component. This takes a few props. We are going to set the &lt;code&gt;varient&lt;/code&gt; to &lt;code&gt;h1&lt;/code&gt; so we get a &lt;code&gt;&amp;lt;h1 /&lt;/code&gt;&amp;gt; rendered, and give it a color to use. This is one of our colors we set up in the theme palette.&lt;/p&gt;

&lt;p&gt;We're also going to use a &lt;code&gt;Divider&lt;/code&gt; which will create a &lt;code&gt;&amp;lt;hr /&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Typography from "@material-ui/core/Typography";
import Divider from '@material-ui/core/Divider';

const TitleBlock = ({title}) =&amp;gt; (
    &amp;lt;&amp;gt;
        &amp;lt;Typography variant={"h1"} color={"primary"} &amp;gt;{title}&amp;lt;/Typography&amp;gt;
        &amp;lt;Divider /&amp;gt;
    &amp;lt;/&amp;gt;
)

export default TitleBlock

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can import it and use it in the &lt;code&gt;App.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThemeProvider } from '@material-ui/styles';

import theme from "./theme";
import TitleBlock from "./Components/TitleBlock";

const App = () =&amp;gt; {
return (
    &amp;lt;ThemeProvider theme={theme} &amp;gt;
        &amp;lt;TitleBlock title="Material UI Playground" /&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
);
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K6ITyuyh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/mui-titleblock.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K6ITyuyh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/mui-titleblock.png" alt="TitleBlock render" width="880" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Material UI contains a full set of built in components used to build a website from Layout to Forms.&lt;/p&gt;

&lt;p&gt;There is the more core components like &lt;code&gt;Typography&lt;/code&gt;, &lt;code&gt;Container&lt;/code&gt; and &lt;code&gt;Link&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Typography&lt;/code&gt; component which we have just used which has a whole range of variants that allow all headings &amp;amp; paragraphs.&lt;/li&gt;
&lt;li&gt;Layout components like &lt;code&gt;Box&lt;/code&gt; and &lt;code&gt;Container&lt;/code&gt; for building wrappers and containers.&lt;/li&gt;
&lt;li&gt;Interactive components like &lt;code&gt;Link&lt;/code&gt; replace &lt;code&gt;&amp;lt;a /&amp;gt;&lt;/code&gt; tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then there are more bespoke components like &lt;code&gt;Breadcrumb&lt;/code&gt; and &lt;code&gt;Paper&lt;/code&gt;. These are prebuilt components that allow you to build the detail of your component more quickly in a uniform design language.&lt;/p&gt;

&lt;p&gt;However if you find that there is a component that you need to build yourself, you are not just stuck with what is provided. There are also tools so that you can style your own components and create the styles for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making styles
&lt;/h2&gt;

&lt;p&gt;Styling is done using &lt;code&gt;JSS&lt;/code&gt;. It's like &lt;code&gt;CSS&lt;/code&gt; but done with JavaScript objects and the JavaScript names for properties (&lt;code&gt;camelCase&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling child elements
&lt;/h3&gt;

&lt;p&gt;We have created a theme and it can be used by the built in components, but what if we have some user generated content or there is some markup that you don't want to convert directly to use MUI components. This is where you can use styles.&lt;/p&gt;

&lt;p&gt;Let's say we have the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const UserContent = () =&amp;gt; {

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h3&amp;gt;I am a user comment&amp;lt;/h3&amp;gt;
            &amp;lt;p&amp;gt;Here is a list:&amp;lt;/p&amp;gt;
            &amp;lt;ul&amp;gt;
                &amp;lt;li&amp;gt;Point A&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;Point 2&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;Final point&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;The end!&amp;lt;/li&amp;gt;
            &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default UserContent;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GYA9agQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/usercontent-unstyled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GYA9agQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/usercontent-unstyled.png" alt="Unstyled user content" width="880" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First we want to create some styles in the &lt;code&gt;UserContent&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import makeStyles from "@material-ui/core/styles/makeStyles";

// Takes the theme we have used in the ThemeProvider
const useStyles = makeStyles((theme) =&amp;gt; ({
    userContentStyles : {
        // Create a catch all style for all elements so we don't
        // need to create each HTML element
        // we use the body1 property which is for &amp;lt;p /&amp;gt;
        // the theme.typography.body1 is an object so this can be added straight on the key
        "&amp;amp; *": theme.typography.body1,
        // To style the &amp;lt;h3 /&amp;gt; we can create a style for it.
        "&amp;amp; h3": {
            // because we are using 2 properties in here we need to wrap them in braces. 
            // We also have to spread the typography object.
            ...theme.typography.h3,
            // Then we can just apply the color using the theme's color object which is a string.
            color: theme.palette.secondary.main
        },
        "&amp;amp; li": {
            // To apply some spacing to our list we could use some spacing properties
            marginBottom: theme.spacing(4)
        },
    }
}));
// ... rest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the &lt;code&gt;h3&lt;/code&gt; element we have to spread the typography object because it contains things like font-sizes, line-height, weight etc. When it is the only value we're using for a key (eg &lt;code&gt;&amp;amp; *&lt;/code&gt;) you don't need to spread it!&lt;/p&gt;

&lt;p&gt;If you wanted to pick only certain aspects of the h3 typography object you could just pull out the values you need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"&amp;amp; h3": {
    fontWeight: theme.typography.body1.fontWeight, // pseudo code: Not sure these are the prop names!
    lineHeight: theme.typography.body1.lineHeight, // pseudo code: Not sure these are the prop names!
    // and so on!
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the palette properties we don't need to spread them because they are just a value so we can use it as is.&lt;/p&gt;

&lt;p&gt;Now we can use the styles in the component itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const UserContent = () =&amp;gt; {
    // Then we get the created styles in the component
    const styles = useStyles()

    // Add them as a className. Note we use the "userContentStyles" key of the styles object
    return (
        &amp;lt;div className={styles.userContentStyles}&amp;gt;
            &amp;lt;h3&amp;gt;I am a user comment&amp;lt;/h3&amp;gt;
            &amp;lt;p&amp;gt;Here is a list:&amp;lt;/p&amp;gt;
            &amp;lt;ul&amp;gt;
                &amp;lt;li&amp;gt;Point A&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;Point 2&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;Final point&amp;lt;/li&amp;gt;
                &amp;lt;li&amp;gt;The end!&amp;lt;/li&amp;gt;
            &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default UserContent;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TOr5jp1F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/usercontent-styled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TOr5jp1F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/usercontent-styled.png" alt="Styled user content" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I am making very obvious changes for illustrative purposes. My UI skills are better than the above design!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Classes for each element
&lt;/h3&gt;

&lt;p&gt;The other way we can style child elements is using class names for all elements.&lt;/p&gt;

&lt;p&gt;Make a new component &lt;code&gt;Classy/index.jsx&lt;/code&gt;. First we import the &lt;code&gt;makeStyles&lt;/code&gt; and use this to define a style function that uses the theme to return our styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import makeStyles from "@material-ui/core/styles/makeStyles";

 // Takes the theme we have used in the ThemeProvider
 const useStyles = makeStyles((theme) =&amp;gt; ({
     // These keys will be used to define the classes for components/elements
    wrapper: {
        // set the properties using theme values
        padding: theme.spacing(1),
        background: theme.palette.secondary.light
    },
     heading: {
         color: theme.palette.secondary.contrastText,
         ...theme.typography.h4, // spread objects
     },
     content: {
        padding: theme.spacing(2),
         color: theme.palette.primary.contrastText,
         background: theme.palette.primary.dark,
         // We can also target nested children as per the other example
         "&amp;amp; *": theme.typography.body1,
     }
 }));
// ..rest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example the &lt;code&gt;wrapper&lt;/code&gt;, &lt;code&gt;heading&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt; are all going to be propeties of the style object we create so we can then use them as our classes. We do this by creating the style object in the component as &lt;code&gt;styles&lt;/code&gt; and then we can use &lt;code&gt;styles.PROPRTY_NAME&lt;/code&gt; to use the className that the JSS/makeStyles has created for us, like so:&lt;/p&gt;

&lt;p&gt;You should have noticed that we have mixed both classes on multiple components (&lt;code&gt;styles.WHATEVER&lt;/code&gt;), and child component styling (&lt;code&gt;&amp;amp; *&lt;/code&gt;). That gives us great flexibility to style what we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Classy = () =&amp;gt; {
    // Create the styles object
    const styles = useStyles()

    // Then use the keys we created above to apply the classes to the right elements
    return (
        &amp;lt;div className={styles.wrapper}&amp;gt;
            &amp;lt;h4 className={styles.heading}&amp;gt;I am styled by the "styles.heading".&amp;lt;/h4&amp;gt;
            &amp;lt;div className={styles.content}&amp;gt;
                &amp;lt;p&amp;gt;Hello! 👋. This element is styled by the nested selector "&amp;amp; *".&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default Classy;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import it into your &lt;code&gt;App.js&lt;/code&gt; and add it to the component and you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nBQ4GPfd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/styles-jss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nBQ4GPfd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/styles-jss.png" alt="Styled components using JSS" width="880" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That covers child components, but what about global elements!&lt;/p&gt;

&lt;h2&gt;
  
  
  Global styles
&lt;/h2&gt;

&lt;p&gt;So far we have seen how to style components and their descendants. But what about if you need to apply style to the &lt;code&gt;html&lt;/code&gt; or &lt;code&gt;body&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;In this case you still create a component, however you can style &lt;a href="https://material-ui.com/customization/components/#global-css-override"&gt;Global elements&lt;/a&gt; by creating a &lt;code&gt;@global&lt;/code&gt; style. Anything within this is applied to the the root and becomes global. Within this you can add your selectors such as &lt;code&gt;body&lt;/code&gt; and apply the styles you need.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: By default, MUI already has &lt;a href="https://material-ui.com/components/css-baseline/"&gt;normalised styles&lt;/a&gt; applied to elements to keep all browsers consistent.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import withStyles from "@material-ui/core/styles/withStyles";

const GlobalCss = withStyles({
    '@global': { // Declare a @global selector in order for jss to pull out the styles
        "html, body": { // create your styles to apply globally
            margin: 0,
            padding: 0,
            backgroundColor: "#pink"
        },
    },
})(() =&amp;gt; null);

export default GlobalCss;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then import and use it in your App:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ThemeProvider } from '@material-ui/styles';

import theme from "./theme";
import TitleBlock from "./Components/TitleBlock";
import UserContent from "./Components/UserContent";
import GlobalCss from "./Components/GlobalCSS";

const App = () =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;GlobalCss /&amp;gt;
      &amp;lt;ThemeProvider theme={theme} &amp;gt;
          &amp;lt;TitleBlock title="Material UI Playground" /&amp;gt;
          &amp;lt;UserContent /&amp;gt;
      &amp;lt;/ThemeProvider&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MFN-cXSH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/mui-global-styled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MFN-cXSH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/mui-global-styled.png" alt="Global themed" width="880" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also create global styles for the MUI component classNames in the &lt;code&gt;GlobalCSS&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const GlobalCss = withStyles({
    '@global': { // Declare a @global selector in order for jss to pull out the styles
        "html, body": { // create your styles to apply globally
            margin: 0,
            padding: 0,
            backgroundColor: "pink"
        },
        // Target the MUI class
        ".MuiTypography-h1": {
            textDecoration: "underline" // A silly style to make a point!
        }
    },
})(() =&amp;gt; null);

export default GlobalCss;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--grnUIUhB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/silly-point.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--grnUIUhB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/silly-point.png" alt="silly point" width="880" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested themes
&lt;/h3&gt;

&lt;p&gt;With nested themes you need to target classes via the more generic &lt;code&gt;[class*="MuiTypography-h1"]&lt;/code&gt; if you wish to style all of an element.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;[class*="CLASSNAME"]&lt;/code&gt; selector selects all classes that contain a value of &lt;code&gt;CLASSNAME&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We need to use this because the nested theme class names change from &lt;code&gt;MuiTypography-h1&lt;/code&gt; to &lt;code&gt;MuiTypography-h1-9999&lt;/code&gt; where &lt;code&gt;9999&lt;/code&gt; is a unique theme number! We are selecting all classes that contains &lt;code&gt;MuiTypography-h1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const GlobalCss = withStyles({
    '@global': {
        // ... the rest
        ".MuiTypography-h1": { // only targets the to level / single theme
            textDecoration: "underline"
        },
        '[class*="MuiTypography-h1"]': { // Targets all themes due to selector
            fontSize: "1em"
        }
    },
})(() =&amp;gt; null);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am using the same theme twice just to demo the class name changes, but you would use a secondary theme in the real world™️!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ThemeProvider theme={theme} &amp;gt; // Top level theme
  &amp;lt;TitleBlock title="Material UI Playground" /&amp;gt; // uses only top level theme
  &amp;lt;ThemeProvider theme={theme} &amp;gt; // Nested theme
      &amp;lt;TitleBlock title="Material UI extra themed" /&amp;gt; // Uses nested theme overides and top level theme
      &amp;lt;UserContent /&amp;gt;
  &amp;lt;/ThemeProvider&amp;gt;
&amp;lt;/ThemeProvider&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DYPEYUl2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/themeGlobal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DYPEYUl2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/react-material-ui-mui/themeGlobal.png" alt="Nested theme overrides" width="880" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The end!
&lt;/h2&gt;

&lt;p&gt;And there you have it. You can now style everything from the built in components, through to nested child components and global styles too.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Formatting percentage values with JavaScript Intl</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Tue, 05 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/formatting-percentage-values-with-javascript-intl-2kgk</link>
      <guid>https://dev.to/designer023/formatting-percentage-values-with-javascript-intl-2kgk</guid>
      <description>&lt;p&gt;Javascript contains an Internationalization API that you can use to format numbers to a user's locale. Setting the style to &lt;code&gt;percent&lt;/code&gt; is all that is needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intl.NumberFormat("en-GB", { style: "percent"}).format(1.2345); // =&amp;gt; "123%"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: A value of &lt;code&gt;1&lt;/code&gt; will return &lt;code&gt;100%&lt;/code&gt; because &lt;code&gt;1x anything&lt;/code&gt; is 100%!&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 * 100 = 100; // 100% 
1.5 * 100 = 150; // 150%   

&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;To specify the decimal places use &lt;code&gt;minimumFractionDigits&lt;/code&gt; and &lt;code&gt;maximumFractionDigits&lt;/code&gt; to give you more visual control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intl.NumberFormat("en-GB", { style: "percent", 
    minimumFractionDigits: 1,
    maximumFractionDigits: 2
}).format(123.45); // =&amp;gt; "123.45%"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can all be wrapped in a function to save repetition and keep consistency across your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const formatPercentage = (value, locale = "en-GB") =&amp;gt; {
    return Intl.NumberFormat(locale, {
        style: "percent",
        minimumFractionDigits: 1,
        maximumFractionDigits: 2
    }).format(value);
};

formatPercentage(1); // =&amp;gt; "100.0%"
formatPercentage(0.1); // =&amp;gt; "10.0%"
formatPercentage(0.13); // =&amp;gt; "13.0%"
formatPercentage(0.135); // =&amp;gt; "13.5%"
formatPercentage(0.1357); // =&amp;gt; "13.57%"
formatPercentage(0.13579); // =&amp;gt; "13.58%"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Formatting currency with JavaScript Intl</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Thu, 17 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/formatting-currency-with-javascript-intl-2ak1</link>
      <guid>https://dev.to/designer023/formatting-currency-with-javascript-intl-2ak1</guid>
      <description>&lt;p&gt;Javascript's Internationalization API allows you to format currency. It takes the locale and options to configure the currency we're using. The options require the style to be set to &lt;code&gt;currency&lt;/code&gt; and a currency option to specify the unit type, as this is returned as part of the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intl.NumberFormat("LOCALE", { style: "currency", currency: "CURRENCY UNIT"}).format(VALUE); // =&amp;gt; "€1.23"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following example formats a &lt;code&gt;EUR&lt;/code&gt; (Euro) currency of the value &lt;code&gt;1.2345&lt;/code&gt; into a &lt;code&gt;en-GB&lt;/code&gt; (English - Great Britain) format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intl.NumberFormat("en-GB", { style: "currency", currency: "EUR"}).format(1.2345); // =&amp;gt; "€1.23"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To save configureing this each time we use the formatting it can be wrapped in a function and the values passes to it. This example tahe the value and optionally a locale, defaulting to &lt;code&gt;en-GB&lt;/code&gt;. It returns a &lt;code&gt;USD&lt;/code&gt; value formatted to the input locale.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const formatUSDCurrency = (value, locale = "en-GB") =&amp;gt; {
    return Intl.NumberFormat(locale, {
        style: "currency",
       currency: 'USD',
    }).format(value);
};


formatUSDCurrency(1230000) // =&amp;gt; "US$1,230,000.00"
formatUSDCurrency(1230000, "de-DE") // =&amp;gt; "1.230.000,00 $"
formatUSDCurrency(1230000, "fr-FR") // =&amp;gt; "1 230 000,00 $US"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat"&gt;configure significant digits, plus/minus signs and other settings&lt;/a&gt; as needed.&lt;/p&gt;

&lt;p&gt;You can either specify the locale yourself, as I have done above, or you can &lt;a href="https://dev.to/articles/javascript-user-locale/"&gt;get the user locale from the browser&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Strava API activity sync with Axios, React, Redux &amp; Saga - part 2</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Wed, 16 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/strava-api-activity-sync-with-axios-react-redux-saga-part-2-48ai</link>
      <guid>https://dev.to/designer023/strava-api-activity-sync-with-axios-react-redux-saga-part-2-48ai</guid>
      <description>&lt;p&gt;With the auth and state setup from &lt;a href="https://dev.to/articles/strava-react-redux-saga-part-1/"&gt;part 1 of "Strava API activity sync with Axios, React, Redux &amp;amp; Saga"&lt;/a&gt; we can now progress to getting the activities and displaying them in a table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Now we have tokens we can start to get the data from Strava. The steps we will take cover the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;code&gt;moment&lt;/code&gt; to handle date based checks&lt;/li&gt;
&lt;li&gt;Create constants for getting activities&lt;/li&gt;
&lt;li&gt;Create the reducers to handle the results of getting the activities&lt;/li&gt;
&lt;li&gt;Using Sagas to retrieve the activities from Strava's API, including;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Auth&lt;/code&gt; - Checking the token is valid and if not getting a new one&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Activities&lt;/code&gt; - Retrieving multiple pages of data&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Display the activity data on the landing page:

&lt;ul&gt;
&lt;li&gt;Creating a &lt;code&gt;Table&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;Using it on the &lt;code&gt;Homepage&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Bonus: Loading states!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling dates
&lt;/h2&gt;

&lt;p&gt;We're going to be doing some date manipulations and checks so let's add a library to help us... &lt;code&gt;Moment&lt;/code&gt; for some date "maths".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add moment

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: At this point in time you can, and probably should, use native JavaScript date objects. However, for the sake of simplicity I am using &lt;code&gt;moment&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create the Activities life cycle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Constants
&lt;/h3&gt;

&lt;p&gt;Firstly we need to create the constants we'll be using to handle the activities. We create one to handle the start of the process, and need a second one which will be used every time we finish loading a page so that we can add the data to our Redux store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/constants/activities.js
export const ACTIVITIES_SYNC_START = "ACTIVITIES_SYNC_START";
export const ACTIVITIES_PAGE_LOADING_COMPLETE = "ACTIVITIES_PAGE_LOADING_COMPLETE";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Actions
&lt;/h3&gt;

&lt;p&gt;We only have one action. It will be linked to a button that triggers our syncing process. We import our constant &lt;code&gt;ACTIVITIES_SYNC_START&lt;/code&gt; and the use it in the action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/constants/actions/activities.js

import { ACTIVITIES_SYNC_START } from "../constants/activities";

export const getActivities = () =&amp;gt; ({
    type: ACTIVITIES_SYNC_START
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reducers
&lt;/h3&gt;

&lt;p&gt;Because the payload we recieve does not include all of the activities in one page we need to merge the existing activity state with the data from each page. We use &lt;code&gt;unionBy&lt;/code&gt; to merge the state based on a property of each activity so that we only keep the unique ones and do not end up with duplicates. We use the activity &lt;code&gt;id&lt;/code&gt; as this is unique across all Strava activities. One side effect of this is that if you update an activity and resync the new changes will never be updated because the &lt;code&gt;id&lt;/code&gt; already exists in our state. Going forward you could write extra actions/reducers/sagas to remedy this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unionBy from "lodash/unionBy";

import { ACTIVITIES_PAGE_LOADING_COMPLETE} from "../constants/activities";

const initialState = {
    activities: [] // we start with no activities
};

export default function (state = initialState, action) {
    switch (action.type) {
        case ACTIVITIES_PAGE_LOADING_COMPLETE:
            // unionBy will merge 2 sets of data by ap property on the objects - our case id
            // if we reimport data at a later date then duplicate ids are ignored!
            const activities = unionBy(state.activities, action.payload, "id");
            return { ...state, activities };
        default:
            return state;
    }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update the &lt;code&gt;src/reducers/index.js&lt;/code&gt; to import and use the &lt;code&gt;activities&lt;/code&gt; reducer we just created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { combineReducers } from "redux";
import { connectRouter } from "connected-react-router";
import auth from "./auth";
import activities from "./activities"; // import this

export default (history) =&amp;gt;
    combineReducers({
        router: connectRouter(history),
        auth,
        activities // and use it!
    });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reboot your app and you should have an empty redux state for activities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;activities : {
    activities: []
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why do we have &lt;code&gt;activities&lt;/code&gt; nested in &lt;code&gt;activities&lt;/code&gt;. It's useful to keep the loading state within the outer activities so that we can show loading spinners and display error states. In our tutorial, all we know is that there is a list of activities and have no idea about what currently being updated by the app. This will be covered in a future tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the token
&lt;/h3&gt;

&lt;p&gt;After a few hours your Strava token will be invalid as it expires. We will use the &lt;code&gt;refresh_token&lt;/code&gt; to get a new access token. Starting in &lt;code&gt;validateAuthTokens()&lt;/code&gt; we check if the current expires timestamp is still in the future and if it is not, then we know that we need a new set of tokens.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;updateRefreshToken&lt;/code&gt; calls the token endpoint using our &lt;code&gt;client_id&lt;/code&gt;, &lt;code&gt;client_secret&lt;/code&gt;, the &lt;code&gt;refresh_token&lt;/code&gt; which will return your new tokens. If you hit this endpoint before the tokens expire then it just returns the current ones. Since you have to wait approx 6 hours for tokens to expire you can manually edit the &lt;code&gt;expiresAt&lt;/code&gt; in the &lt;code&gt;dev tools &amp;gt; application &amp;gt; localstorage&lt;/code&gt; and set it to &lt;code&gt;0&lt;/code&gt; - This will then think the token has expired!&lt;/p&gt;

&lt;p&gt;In the real world™️, the &lt;code&gt;tokenClient&lt;/code&gt; should be on a backend (eg Netlify function or AWS lambda) where the &lt;code&gt;client_secret&lt;/code&gt; is not accessible to the users browser. The token client would be replaced by a &lt;code&gt;backendClient&lt;/code&gt; or &lt;code&gt;secretClient&lt;/code&gt; or similar which would be passed only the &lt;code&gt;refreshToken&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/sagas/auth.js

//... the existing auth.js js file

const updateRefreshToken = () =&amp;gt; {
    // Get the refresh token from localstorage
    const refreshToken = localStorage.getItem("refreshToken");

    // using the token client we then post to the token endpoint with
    // our client_id, client_secret, the refresh_token
    // In a realworld app we would pass our refresh_token to a backend where we can keep the client_secret a secret!
    return tokenClient({
        url: "/token",
        method: "post",
        params: {
            client_id: clientID,
            client_secret: clientSecret,
            refresh_token: refreshToken,
            grant_type: "refresh_token"
        }
    })
        .then((response) =&amp;gt; {
            // return the new tokens and timestamps to be handled
            return response.data;
        })
        .catch((error) =&amp;gt; {
            throw new Error(error);
        });
};

const tokenIsValid = () =&amp;gt; {
    // get the existing token from localstorage
    const expiresAt = localStorage.getItem("expiresAt");
    // create a timestamp for now down to the second
    const now = Math.floor(Date.now() / 1000);
    // Check if the expires timestamp is less than now - meaning it's in the past and has expired
    return now &amp;lt; expiresAt;
};

export function* validateAuthTokens() {
    // 1. call the function to check if the token is valid
    const validToken = yield call(tokenIsValid);

    // 2. If the token is invalid then start the process to get a new one
    if (!validToken) {
        // call the function to get new refresh tokens
        const data= yield call(updateRefreshToken);
        // put the action to handle the token updates (which stores them in localstorage)
        yield put(updateAuthTokens(data));
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Activities Saga
&lt;/h3&gt;

&lt;p&gt;Now that we have a way to update the token and keep our client valid we can create our activities saga. Once triggered, this will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check/update the auth tokens (with the tokenClient we just setup)&lt;/li&gt;
&lt;li&gt;Starting at an &lt;code&gt;epoch&lt;/code&gt; and page 1 it will step through all pages of activities

&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;apiClient&lt;/code&gt; to retrieve each page until it reaches a page with an empty list, meaning we have reached the end.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import get from "lodash/get";
import moment from "moment";
import { call, put, select, takeLeading } from "redux-saga/effects";

import { apiClient } from "../../api";
import { validateAuthTokens} from "./auth";
import {ACTIVITIES_PAGE_LOADING_COMPLETE, ACTIVITIES_SYNC_START} from "../constants/activities";

const getActivity = (epoch, page = 1) =&amp;gt; {
    // Get the data from Strava starting from `epoch` and with the page number of `page`
    return apiClient({
        url: `/athlete/activities?per_page=30&amp;amp;after=${epoch}&amp;amp;page=${page}`,
        method: "get"
    })
        .then((response) =&amp;gt; {
            // return the data
            return response;
        })
        .catch((err) =&amp;gt; {
            throw err;
        });
};

const getLastActivityTimestamp = (state) =&amp;gt; {
    // The epoch is dependant on the last activity date if we have one
    // This is so that we only get activities that we don't already have 
    const lastActivity = state.activities.activities.slice(-1)[0];
    const lastDate = get(lastActivity, "start_date");

    if (lastDate) {
        // return the unix timestamp of the last date
        return moment(lastDate).unix();
    }

    // Manually set a year (either in the env or default to 2019)
    // I have set a recent one otherwize it's a big first sync
    // And if there are a LOT of activities then you may run out of local storage!
    const initialYear = Number(process.env.REACT_APP_INITIALYEAR || 2019);

    // Create a timestamp for this year
    return moment().year(initialYear).startOf("year").unix();
};

function* updateAthleteActivity() {
    // 1. Check the tokens are valid and update as needed
    yield call(validateAuthTokens);

    // 2. set the page and epoch - The epoch depends on the last activity date
    let page = 1;
    const epoch = yield select(getLastActivityTimestamp);

    try {
        while (true) {
            // Loop through pages until we manually break the cycle
            // Start the process of getting a page from Strava
            const { data } = yield call(getActivity, epoch, page);

            // Page has no activities - Last page reached
            if (!data.length) {
                // If the result has an empty array (no activities) then end the sync - we must have them all!
                break;
            } else {
                // put the data into redux and let the activity reducer merge it
                yield put({ type: ACTIVITIES_PAGE_LOADING_COMPLETE, payload: data });

                // Next slide please!
                page += 1;
            }
        }
    } catch (e) {
        yield console.log(e)
    }

}

export function* watchUpdateAthleteActivitiesAsync() {
    yield takeLeading(ACTIVITIES_SYNC_START, updateAthleteActivity);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the new saga to the root saga so we can start to listen for &lt;code&gt;ACTIVITIES_SYNC_START&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file src/redux/sagas/index.js
import { all } from "redux-saga/effects";

import { beginStravaAuthAsync, validateStravaTokenAsync } from "./auth";
import { watchUpdateAthleteActivitiesAsync} from "./activities"; // Add

// single entry point to start all Sagas at once
export default function* rootSaga() {
    yield all([ 
                beginStravaAuthAsync(), 
                validateStravaTokenAsync(), 
                watchUpdateAthleteActivitiesAsync() // Add
            ]);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the "front end"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Display activities in a table
&lt;/h3&gt;

&lt;p&gt;Create a table component. This takes an array of activities and renders the title.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Table = ({activities}) =&amp;gt; {

    return (
        &amp;lt;table&amp;gt;
            &amp;lt;thead&amp;gt;
                &amp;lt;tr&amp;gt;
                    &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
                &amp;lt;/tr&amp;gt;
            &amp;lt;/thead&amp;gt;
            &amp;lt;tbody&amp;gt;
                {activities.map(activity =&amp;gt; (
                    &amp;lt;tr key={activity.id}&amp;gt;
                        &amp;lt;td&amp;gt;{activity.name}&amp;lt;/td&amp;gt;
                    &amp;lt;/tr&amp;gt;
                ))}
            &amp;lt;/tbody&amp;gt;
        &amp;lt;/table&amp;gt;
    )
}

export default Table;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a lot more that can be displayed if you wish!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    resource_state:2
    name:"Night Ride"
    distance:20800.4
    moving_time:4649
    elapsed_time:4649
    total_elevation_gain:96
    type:"Ride"
    workout_type:null
    id:398412246
    external_id:"Ride390811004.gpx"
    upload_id:447540868
    start_date:"2010-01-01T00:00:00Z"
    start_date_local:"2010-01-01T00:00:00Z"
    timezone:"(GMT+00:00) Europe/London"
    utc_offset:0
    location_city:"Malpas"
    location_state:null
    location_country:"United Kingdom"
    start_latitude:55.06
    start_longitude:-1.68
    achievement_count:0
    kudos_count:0
    comment_count:0
    athlete_count:1
    photo_count:0
    trainer:false
    commute:false
    manual:false
    private:false
    visibility:"everyone"
    flagged:false
    gear_id:"b1131808"
    from_accepted_tag:false
    upload_id_str:"447540868"
    average_speed:4.474
    max_speed:5.4
    average_watts:106.7
    kilojoules:496
    device_watts:false
    has_heartrate:false
    heartrate_opt_out:false
    display_hide_heartrate_option:false
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Home page
&lt;/h3&gt;

&lt;p&gt;We need to import our &lt;code&gt;syncActivities&lt;/code&gt; action and our &lt;code&gt;Table&lt;/code&gt; into the &lt;code&gt;HomePage&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useSelector, useDispatch } from "react-redux";

import {beginStravaAuthentication} from "../redux/actions/auth";
import {syncActivities} from "../redux/actions/activities"; // New
import Table from "../components/table"; // New

const HomePage = () =&amp;gt; {
    const auth = useSelector((state) =&amp;gt; state.auth);
    const activities = useSelector((state) =&amp;gt; state.activities.activities); // New: Get the activities from the state

    const dispatch = useDispatch();

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;Home&amp;lt;/h1&amp;gt;

            { auth.isAuthenticated ? (
                &amp;lt;&amp;gt;
                    &amp;lt;h2&amp;gt;Logged in&amp;lt;/h2&amp;gt;
                    {/* Add a button to dispatch our syncActivities action */}
                    &amp;lt;button type="button" onClick={() =&amp;gt; dispatch(syncActivities())}&amp;gt;Sync activities&amp;lt;/button&amp;gt;

                    {/* Display the activities in the table */}
                    &amp;lt;Table activities={activities} /&amp;gt;
                &amp;lt;/&amp;gt;
            ): (
                // add the dispatch to the button onClick
                &amp;lt;button type="button" onClick={() =&amp;gt; dispatch(beginStravaAuthentication())}&amp;gt;Authorise on Strava&amp;lt;/button&amp;gt;
            )}
        &amp;lt;/div&amp;gt;
    )
}

export default HomePage;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this added you can now sync your activities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bwq9HYbr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-part-2/table.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bwq9HYbr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-part-2/table.png" alt="Table rendered" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you do this you will notice that the list slowly starts to grow as it syncs and the only way you know that it is complete is that it stops growing after a while (and you can see your latest activities!).&lt;/p&gt;

&lt;h3&gt;
  
  
  Some extra bonuses
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Save the activities
&lt;/h4&gt;

&lt;p&gt;Now that we have activities it would be nice if they are saved on a refresh! Open up your &lt;code&gt;configureStore&lt;/code&gt; and update the saveState to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;saveState({
    auth: store.getState().auth,
    activities: store.getState().activities // Additional state to save
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Show a loading state
&lt;/h4&gt;

&lt;p&gt;To add a loading state to the activities we need to add a loading state. First add a new constant to handle this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/constants/activities.js
export const ACTIVITIES_SYNC_STATE = "ACTIVITIES_SYNC_STATE";
// ... the rest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add an acompanying action. This will take a payload as an argument that we can use to update the state. In our case we will be passing it something like &lt;code&gt;{loading: true}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/actions/activities.js
export const setActivitiesState = (data) =&amp;gt; ({
    type: ACTIVITIES_SYNC_STATE,
    payload: data
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now need to update our reducers to handle this new action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/reducers/activities.js
import unionBy from "lodash/unionBy";

import { ACTIVITIES_PAGE_LOADING_COMPLETE, ACTIVITIES_SYNC_STATE } from "../constants/activities"; // add ACTIVITIES_SYNC_STATE

const initialState = {
    activities: [],
    loading: false // add the default loading state (false)
};

export default function (state = initialState, action) {
    switch (action.type) {
        // Add the action here
        case ACTIVITIES_SYNC_STATE: 
            // merge our payload in
            return { ...state, ...action.payload}

        case ACTIVITIES_PAGE_LOADING_COMPLETE:
            const activities = unionBy(state.activities, action.payload, "id");
            return { ...state, activities };

        default:
            return state;
    }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our saga must now be updated to put the actions into redux so we can change the &lt;code&gt;loading&lt;/code&gt; state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/sagas/activities.js
// ... other imports

import { setActivitiesState} from "../actions/activities"; // import the state new action

// ... the rest

function* updateAthleteActivity() {
    // Add the put for ACTIVITIES_SYNC_STATE and we pass a payload of loading: true
    yield put(setActivitiesState({loading: true})) 

    yield call(validateAuthTokens);

    let page = 1;
    const epoch = yield select(getLastActivityTimestamp);
    try {
        while (true) {
            const { data } = yield call(getActivity, epoch, page);

            // Page has no activities - Last page reached
            if (!data.length) {
                break;
            } else {
                yield put({ type: ACTIVITIES_PAGE_LOADING_COMPLETE, payload: data });
                page += 1;
            }
        }
    } catch (e) {
        yield console.log(e)
    }

    // I like to add a small delay to the final action just so that the loading state is visible for a short while
    // so that users *feel* like something has happened and don't miss it!
    yield delay(1000)
    // Add the put for ACTIVITIES_SYNC_STATE and we pass a payload of loading: false
    // This sets it back to the non loading state and the table should show with everything in!
    yield put(setActivitiesState({loading: false}))
}

// ... the rest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the &lt;code&gt;HomePage&lt;/code&gt; update the activities state to it returns the full activity object so we can hook into the loading state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Old
    // const activities = useSelector((state) =&amp;gt; state.activities.activities);

    // New
    const activities = useSelector((state) =&amp;gt; state.activities);


    // Old
    // &amp;lt;Table activities={activities} /&amp;gt;

    // New
    &amp;lt;Table data={activities} /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, update the &lt;code&gt;Table&lt;/code&gt; component to take the new data prop, then extract the loading state and activity data, and update the render to display a loading state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Table = ({ data }) =&amp;gt; {

    const { loading, activities } = data;

    return (
        &amp;lt;&amp;gt;
            {loading ? (
                &amp;lt;div&amp;gt;
                    &amp;lt;h3&amp;gt;Loading...&amp;lt;/h3&amp;gt;
                &amp;lt;/div&amp;gt;
            ) : (
                &amp;lt;table&amp;gt;
                    &amp;lt;thead&amp;gt;
                    &amp;lt;tr&amp;gt;
                        &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
                    &amp;lt;/tr&amp;gt;
                    &amp;lt;/thead&amp;gt;
                    &amp;lt;tbody&amp;gt;
                    {activities.map(activity =&amp;gt; (
                        &amp;lt;tr&amp;gt;
                            &amp;lt;td&amp;gt;{activity.name}&amp;lt;/td&amp;gt;
                        &amp;lt;/tr&amp;gt;
                    ))}
                    &amp;lt;/tbody&amp;gt;
                &amp;lt;/table&amp;gt;
            )}
        &amp;lt;/&amp;gt;
    )
}

export default Table;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;And that's it! In the final part of the Strava API app (coming soon) we will move our &lt;code&gt;client_secret&lt;/code&gt; secret to a Netlify function to keep things safer.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Strava API activity sync with Axios, React, Redux &amp; Saga - part 1</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Sun, 13 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/strava-api-activity-sync-with-axios-react-redux-saga-part-1-45lo</link>
      <guid>https://dev.to/designer023/strava-api-activity-sync-with-axios-react-redux-saga-part-1-45lo</guid>
      <description>&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;I've been using Strava for over 5 years to track my activities, and more recently analyse them too. Towards the end of 2019 I realised that I was getting close to running 1,000km in a year, so I started a basic spreadsheet to track the data so that I could plan my runs accordingly and hit my goal. The spreadsheet was a basic setup that just plotted distance run vs distance I needed to run, but it got me to the end of the year, and I managed to pass my target. As 2020 began I started to update the spreadsheet to calculate more detailed stats, and more in-depth analysis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tUX_Q4Ri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/spreadsheetv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tUX_Q4Ri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/spreadsheetv2.png" alt="Google Sheets Spreadsheet v2" width="880" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding activities manually every time I completed them is tedious and more importantly, minor disctane descrepacies were making my analysis inaccurate - Although my entries are only a few metres out per activity it all adds up after a hundred or so and the analysis ends up wrong!&lt;/p&gt;

&lt;p&gt;Autotomation of the activity entries is the best and easiest way to manage my data. There are &lt;a href="https://scottpdawson.com/export-strava-workout-data/"&gt;a few scripts out there&lt;/a&gt; that allow downloading of your activities to a CSV, however these also involve a manual step which is not ideal.&lt;/p&gt;

&lt;p&gt;Let's build a web app to use the Strava API to retrieve activity details and store them in Redux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the web app
&lt;/h2&gt;

&lt;p&gt;We're going to do this in a few sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the app and install the core dependencies&lt;/li&gt;
&lt;li&gt;Get a Strava API key/secret&lt;/li&gt;
&lt;li&gt;Setup the API client&lt;/li&gt;
&lt;li&gt;Get the Auth tokens&lt;/li&gt;
&lt;li&gt;Get some activities (part 2)&lt;/li&gt;
&lt;li&gt;Display activities in a table (part 2)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Create the app and install the dependencies
&lt;/h3&gt;

&lt;p&gt;First we want to get the core app setup, and the needed dependencies installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx npx create-react-app strava-api-app

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After it creates the app and installs the dependencies go to the directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd strava-api-app

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to install everything that we need. I'm using &lt;code&gt;yarn&lt;/code&gt; but &lt;code&gt;npm&lt;/code&gt; is also good!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add redux react-redux react-router react-router-dom connected-react-router redux-saga history@4.10.1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Setup the store
&lt;/h4&gt;

&lt;p&gt;Create the Auth state &lt;code&gt;src/redux/reducers/auth.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/reducers/auth.js
const initialState = {
    isAuthenticated: false,
};

export default function (state = initialState, action) {
    if (!action) return state;

    switch (action.type) {
        default:
            return state;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a reducers index file &lt;code&gt;src/redux/reducers/index.js&lt;/code&gt;. Here we will add our reducers to handle the app data. For now we will just add the core reducers that we start with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/reducers/index.js
import { combineReducers } from "redux";
import { connectRouter } from "connected-react-router";
import auth from "./auth";

export default (history) =&amp;gt;
    combineReducers({
        router: connectRouter(history),
        auth
    });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a Saga index file &lt;code&gt;src/redux/sagas/index.js&lt;/code&gt;. For now this will do nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/sagas/index.js

// single entry point to start all Sagas at once
export default function* rootSaga() {

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file in the &lt;code&gt;src&lt;/code&gt; directory named &lt;code&gt;configureStore.js&lt;/code&gt;. We will import our reducers and sagas into the store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/configureStore.js
import { createBrowserHistory } from "history";
import { applyMiddleware, compose, createStore } from "redux";
import { routerMiddleware } from "connected-react-router";
import createSagaMiddleware from "redux-saga";

import createRootReducer from "./redux/reducers";
import rootSaga from "./redux/sagas";

const sagaMiddleware = createSagaMiddleware();
export const history = createBrowserHistory();

const configureStore = (initialState = {}) =&amp;gt; {
    const enhancers = [];
    const __DEV__ = process.env.NODE_ENV !== "production";
    if ( __DEV__ ) {
        const { devToolsExtension } = window;
        if (typeof devToolsExtension === "function") {
            enhancers.push(devToolsExtension());
        }
    }

    const middleware = [routerMiddleware(history), sagaMiddleware];
    const rootReducer = createRootReducer(history);

    const store = createStore(rootReducer, initialState, compose(applyMiddleware(...middleware), ...enhancers));

    sagaMiddleware.run(rootSaga);

    return store;
};

export default configureStore();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally in the index file we need to make some updates. First add a few imports so we can use the store. Then update the render to add the &lt;code&gt;&amp;lt;Provider&amp;gt;&amp;lt;/Provider&amp;gt;&lt;/code&gt; with the store, and the &lt;code&gt;&amp;lt;ConnectedRouter&amp;gt;&amp;lt;/ConnectedRouter&amp;gt;&lt;/code&gt; with the history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/index.jsx

// Add
import { ConnectedRouter } from "connected-react-router";
import { Provider } from "react-redux";
import storeConfig, { history } from "./configureStore";

// Update
ReactDOM.render(
    &amp;lt;React.StrictMode&amp;gt;
        &amp;lt;Provider store={store}&amp;gt;
                &amp;lt;ConnectedRouter history={history}&amp;gt;
                    &amp;lt;App /&amp;gt;
                &amp;lt;/ConnectedRouter&amp;gt;
        &amp;lt;/Provider&amp;gt;
    &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById('root')
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this you can start the app with &lt;code&gt;yarn start&lt;/code&gt; and when it boots you should see the normal CRA landing page and in the console you should see "Hello!".&lt;/p&gt;

&lt;h3&gt;
  
  
  Get a Strava API key/secret
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Login to Strava&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="https://www.strava.com/settings/api"&gt;the developer API page in your settings&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create an app&lt;/li&gt;
&lt;li&gt;You should see some details for your app. The key ones are &lt;code&gt;Client Secret&lt;/code&gt; and &lt;code&gt;Your Access Token&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HRfQVC-h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/stravaAPI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HRfQVC-h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/stravaAPI.png" alt="Strava API Page" width="880" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup the API client
&lt;/h3&gt;

&lt;p&gt;For the API client we're going to use &lt;a href="https://github.com/axios/axios"&gt;&lt;code&gt;axios&lt;/code&gt;&lt;/a&gt; - It's very similar to the built in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/a&gt; but is more compatible with older browsers out of the box and I prefer the configuration. If you prefer fetch then you can use it but you will need to "translate" the API calls accordingly.&lt;/p&gt;

&lt;p&gt;Create a file for the api configurations in &lt;code&gt;src/api/index.js&lt;/code&gt;. This will contain 2 clients configs and an inteceptor for the apiClient that handles the extra authorization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from "axios";

export const tokenClient = axios.create({
    baseURL: "https://www.strava.com/oauth",
    timeout: 3000
});

const apiClient = axios.create({
    baseURL: "https://www.strava.com/api/v3",
    timeout: 3000
});

apiClient.interceptors.request.use(
    (config) =&amp;gt; {
        const token = localStorage.getItem("accessToken");

        if (token) {
            // eslint-disable-next-line no-param-reassign
            config.headers.Authorization = `Bearer ${token}`;
        }

        return config;
    },
    (error) =&amp;gt; {
        Promise.reject(error);
    }
);

export { apiClient };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get the Auth tokens
&lt;/h3&gt;

&lt;p&gt;The strava API allows a user authorize your app, which redirects back to your app with a &lt;code&gt;token&lt;/code&gt;. The token can then be used to check the required scopes are correct and obtain a short-lived access token, refresh token and expiry date. These tokens are then used to retrieve data or a new token.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: In this tutorial I will be sending my application &lt;code&gt;secret key&lt;/code&gt; from the front end. This is NOT SECURE! The token is visible in the code for all to see. It should be sent by a backend service. In a future post I will cover setting up &lt;code&gt;Netlify functions&lt;/code&gt; to keep the secret a secret. DON'T use ths for production code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's create some constants to be used around the app...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/constants/auth.js
export const STRAVA_AUTH_START = "STRAVA_AUTH_START";
export const STRAVA_AUTH_TOKEN_VALIDATE = "STRAVA_AUTH_TOKEN_VALIDATE";
export const STRAVA_AUTH_TOKEN_UPDATE = "STRAVA_AUTH_TOKEN_UPDATE";
export const STRAVA_AUTH_LOG_OUT = "STRAVA_AUTH_LOG_OUT";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then lets start creating actions for the authentication life cycle... Authenticate, validate, update, deauthenticate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/redux/actions/auth.js
import { STRAVA_AUTH_LOG_OUT, STRAVA_AUTH_START, STRAVA_AUTH_TOKEN_UPDATE, STRAVA_AUTH_TOKEN_VALIDATE } from "../constants/auth";

// To begin auth we request app access from strava and the user logs in. A token is returned
export const beginStravaAuthentication = () =&amp;gt; ({
    type: STRAVA_AUTH_START
});

// The token must be validated against the app client id and secret
export const validateStravaToken = (code) =&amp;gt; ({
    type: STRAVA_AUTH_TOKEN_VALIDATE,
    payload: code
});

// A validated app access only lasts so long. After a while we need to request a new token. 
// When we do the new tokens must be saved
export const updateAuthTokens = ({ refresh_token: refreshToken, expires_at: expiresAt, access_token: accessToken }) =&amp;gt; ({
    type: STRAVA_AUTH_TOKEN_UPDATE,
    payload: {
        isAuthenticated: true,
        refreshToken,
        expiresAt,
        accessToken
    }
});

// A user must be able to log out. When the app recieves this request we will remove all data from storage, remove tokens and deauth the app with Strava.
export const beginStravaDeauthentication = () =&amp;gt; ({
    type: STRAVA_AUTH_LOG_OUT
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;src/pages/HomePage.jsx&lt;/code&gt; component - We will use this to login / out and sync data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const HomePage = () =&amp;gt; {
    return (
        &amp;lt;div&amp;gt;Home&amp;lt;/div&amp;gt;
    )
}

export default HomePage;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;src/pages/Token.jsx&lt;/code&gt; component - This will eventually handle the incoming token from Strava. For now we just add a placeholder contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Token = () =&amp;gt; {
    return (
        &amp;lt;div&amp;gt;Token recieved&amp;lt;/div&amp;gt;
    )
}

export default Token;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up &lt;code&gt;src/App.js&lt;/code&gt; and replace the contents with the following which includes the routes for the initial app and the 2 new components we just created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: src/App.js

import { Route, Switch } from "react-router";

import Token from "./pages/Token";
import HomePage from "./pages/HomePage";

const App = () =&amp;gt; {

  return (
      &amp;lt;div&amp;gt;
        &amp;lt;Switch&amp;gt;
          &amp;lt;Route path="/token" exact component={Token} /&amp;gt;

          &amp;lt;Route path="/" exact component={HomePage} /&amp;gt;
        &amp;lt;/Switch&amp;gt;
      &amp;lt;/div&amp;gt;
  )
}

export default App;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the HomePage to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// import redux hooks
import { useSelector, useDispatch } from "react-redux";
// import the begin auth action
import {beginStravaAuthentication} from "../redux/actions/auth";

const HomePage = () =&amp;gt; {
    // get the auth state (so we can use to hide the button when the user is authenticated)
    const auth = useSelector((state) =&amp;gt; state.auth);
    // create a dispatch function
    const dispatch = useDispatch();

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;Home&amp;lt;/h1&amp;gt;

            {/* Use the auth state to show hide the button */}
            { auth.isAuthenticated ? (
                &amp;lt;h2&amp;gt;Logged in&amp;lt;/h2&amp;gt;
            ): (
                // add the dispatch to the button onClick
                &amp;lt;button type="button" onClick={() =&amp;gt; dispatch(beginStravaAuthentication())}&amp;gt;Authorise on Strava&amp;lt;/button&amp;gt;
            )}
        &amp;lt;/div&amp;gt;
    )
}

export default HomePage;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you click the button then the &lt;code&gt;STRAVA_AUTH_START&lt;/code&gt; action appear in the Redux dev tools. Next we need to handle it.&lt;/p&gt;

&lt;p&gt;Now we need to use the details from the Strava API app we setup earlier.&lt;/p&gt;

&lt;p&gt;In the root create a &lt;code&gt;.env&lt;/code&gt; file with the following, then stop and restart your app or the new ENV variables will not be in your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_STRAVA_CLIENT_ID=your_id
REACT_APP_STRAVA_CLIENT_SECRET=your_secret

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: Again, in this tutorial I will be sending my application &lt;code&gt;secret key&lt;/code&gt; from the front end as &lt;code&gt;REACT_APP_STRAVA_CLIENT_SECRET&lt;/code&gt;. This is NOT SECURE! The token is visible in the code for all to see. It should be sent by a backend service. In a future post I will cover setting up &lt;code&gt;Netlify functions&lt;/code&gt; to keep the secret a secret. DON'T use ths for production code. &lt;code&gt;REACT_APP_STRAVA_CLIENT_ID&lt;/code&gt; is ok to be public and will always remain this way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create &lt;code&gt;src/redux/sagas/auth.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { takeLeading, call } from "redux-saga/effects";
import {STRAVA_AUTH_START} from "../constants/auth";

const clientID = process.env.REACT_APP_STRAVA_CLIENT_ID;

function handOffToStravaAuth() {
    // Get the current address of the app running eg localhost or mycoolapp.io
    const { origin } = window;
    // push the Strava authorise url onto the browser window with our PUBLIC clientID and the return url (origin)
    // Note the redirect_uri=${origin}/token - this is the token page we setup earlier.
    window.location.assign(`https://www.strava.com/oauth/authorize?client_id=${clientID}&amp;amp;redirect_uri=${origin}/token&amp;amp;response_type=code&amp;amp;scope=activity:read_all`);
}

function* beginStravaAuthentication() {
    // A simple generator function
    // Just yeilds one other function - handOffToStravaAuth
    yield call(handOffToStravaAuth);
}

export function* beginStravaAuthAsync() {
    // This will listen for the first STRAVA_AUTH_START and then call beginStravaAuthentication
    yield takeLeading(STRAVA_AUTH_START, beginStravaAuthentication);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you click the &lt;code&gt;Authorise on Strava&lt;/code&gt; button now, nothing will happen as we need to link up the sagas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { all } from "redux-saga/effects";

// Import the new async function we created above
import { beginStravaAuthAsync } from "./auth";

export default function* rootSaga() {
    // Add beginStravaAuthAsync to the rootSaga
    yield all([beginStravaAuthAsync()]);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you click the button:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;beginStravaAuthentication&lt;/code&gt; is dispatched&lt;/li&gt;
&lt;li&gt;The saga middleware handles the action &lt;code&gt;STRAVA_AUTH_START&lt;/code&gt; to every function in the &lt;code&gt;yield all([...])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;beginStravaAuthAsync&lt;/code&gt; responds to it as it's listening for &lt;code&gt;STRAVA_AUTH_START&lt;/code&gt;. &lt;code&gt;takeLeading&lt;/code&gt; means that if you click the button multiple times only the first action is handled until it completes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;beginStravaAuthentication&lt;/code&gt; yields a single function using call - &lt;code&gt;handOffToStravaAuth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handOffToStravaAuth&lt;/code&gt; pushes the authorise url to the browser which loads Strava's auth dialog&lt;/li&gt;
&lt;li&gt;The user authorises the app&lt;/li&gt;
&lt;li&gt;Strava returns the app to the &lt;code&gt;redirect_uri&lt;/code&gt; with a token as a querystring parameter. eg. &lt;code&gt;http://localhost:3000/token?state=&amp;amp;code=ee7c5a... ...1f073&amp;amp;scope=read,activity:read_all&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point we need to handle this token and authorise that it's correct.&lt;/p&gt;

&lt;p&gt;Next we need to install another package to handle querystrings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add query-string

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the Token component we need to update it to handle an incoming token on load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, {useEffect} from "react"
import queryString from "query-string";

const Token = () =&amp;gt; {

    const location = useSelector((state) =&amp;gt; state.router.location);

    useEffect (() =&amp;gt; {
        // pull the code out of the query string
        const {code} = queryString.parse(location.search);

        console.log(code)

    }, [])

    return (
        &amp;lt;div&amp;gt;Token recieved&amp;lt;/div&amp;gt;
    )
}

export default Token;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now get the token from the return query from Strava. You can test this out now from the homepage by &lt;code&gt;Authenticating with Strava&lt;/code&gt; and then logging in and confirming. You will be returned to &lt;code&gt;http://localhost:3000/token?state=&amp;amp;code=ee7c5a... ...1f073&amp;amp;scope=read,activity:read_all&lt;/code&gt;. You should see your code in the console.&lt;/p&gt;

&lt;p&gt;We now need to validate your code. First we import the validation action (and redux dispatch) and then we can dispatch the action when we get the code. We also have a check to see if the app is already validated, and if it is just return to the homepage as we can't validate a code again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, {useEffect} from "react"
import queryString from "query-string";
// New imports
import {push} from "connected-react-router";
import { useSelector, useDispatch } from "react-redux";
import { validateStravaToken } from "../redux/actions/auth"

const Token = () =&amp;gt; {

    const location = useSelector((state) =&amp;gt; state.router.location);
    // Get the current auth state and setup a dispatch action
    const auth = useSelector((state) =&amp;gt; state.auth);
    const dispatch = useDispatch();

    useEffect (() =&amp;gt; {
        // Check if the user/app is authenticated
        if (!auth.isAuthenticated &amp;amp;&amp;amp; code) {
            // pull the code out of the query string
            const {code} = queryString.parse(location.search);
            // dispatch the validation code to be handled by saga
            dispatch(validateStravaToken(code));
        } else {
            // Push back to home page because the user is authenticated or there is no code!
            dispatch(push("/"));
        }

    }, [])

    // Return nothing because this page had nothing to display
    return null;
}

export default Token;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now reauthenticate, or refresh the page if the token code is still in the url then you should see a redux action in the dev tools but nothing else will happen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    type: "STRAVA_AUTH_TOKEN_VALIDATE"
    payload: "ee7c5a... ...1f073"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to create a saga to handle this action type. In your &lt;code&gt;src/redux/sagas/auth.js&lt;/code&gt; we add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { takeLeading, call, put } from "redux-saga/effects"; // Update to add put
import {STRAVA_AUTH_START, STRAVA_AUTH_TOKEN_VALIDATE} from "../constants/auth"; // add STRAVA_AUTH_TOKEN_VALIDATE
import {push} from "connected-react-router"; // add

import {tokenClient} from "../../api"; // add

const clientID = process.env.REACT_APP_STRAVA_CLIENT_ID;
// Add! But remember this should only ever be done for non PROD. 
// The secret should be on a non client app (ie backend or lambda like Netlify)
const clientSecret = process.env.REACT_APP_STRAVA_CLIENT_SECRET; 

// ... the existing sagas

// Add the new sagas

const apiValidateToken = (code) =&amp;gt; {
    return tokenClient({
        url: "/token",
        method: "post",
        params: {
            client_id: clientID,
            client_secret: clientSecret,
            code,
            grant_type: "authorization_code"
        }
    })
        .then((response) =&amp;gt; {
            return response.data;
        })
        .catch((error) =&amp;gt; {
            Promise.reject(error);
        });
};

function* validateStravaToken({ payload: code }) {
    // here you could dispatch a loading start action!

    // we dispatch a api call to validate the tokens and it returns a data object with the values we need.
    const data = yield call(apiValidateToken, code);

    // push an the action type: STRAVA_AUTH_TOKEN_UPDATE with the data
    yield put(updateAuthTokens(data));

    yield put(push("/")); // Push the browser to the root when we are done!

    // here you could dispatch a loading end action!
}

export function* validateStravaTokenAsync() {
    // Listen for STRAVA_AUTH_TOKEN_VALIDATE
    yield takeLeading(STRAVA_AUTH_TOKEN_VALIDATE, validateStravaToken);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note! I cannot stress enough about the client secret. Anything that is on the client side is public as it is in the code that is on a users browser. In the real world™️ the token client is a backend client where the code is not available to the user!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then we need to handle the tokens with a reducer. Update the &lt;code&gt;reducers/auth.js&lt;/code&gt; to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { STRAVA_AUTH_TOKEN_UPDATE } from "../constants/auth"; // Import the constant for the action

const initialState = {
    isAuthenticated: false,
};

export default function (state = initialState, action) {
    if (!action) return state;

    switch (action.type) {
        // The new bits
        case STRAVA_AUTH_TOKEN_UPDATE:
            // save the tokens to the local storage
            localStorage.setItem("refreshToken", action.payload.refresh_token);
            localStorage.setItem("expiresAt", action.payload.expires_at);
            localStorage.setItem("accessToken", action.payload.access_token);

            // update the state to show the app the user is logged in
            return { ...state, isAuthenticated: true}

        default:
            return state;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you authenticate again you should now end back at the homepage and you should see the "Logged in" text and in the application data (dev tools) you can see the tokens we recieved after authenticating. It is these that we will send with subsequent requests to get data or new tokens when our existing token expires.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I50-4Vx4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/tokens.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I50-4Vx4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/tokens.png" alt="Tokens in localstorage" width="880" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our auth redux state should also be showing as &lt;code&gt;isAuthenticated: true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pL5JH8bF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/isAuthenticated.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pL5JH8bF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://carl-topham.com/content/articles/strava-react-redux-saga-part-1/isAuthenticated.png" alt="Redux state for auth isAuthenticated" width="880" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the tokens have been stored, the redux state is not stored, so refreshing the page will reset the store. There are a few ways to persist the state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;localstorage. This has file size limits and will eventually be full and break your app, however it's an good initial step. See below&lt;/li&gt;
&lt;li&gt;IndexDB - The size limit is your machine drive space so I doubt this will run out, however it's more complicated to setup so I'm not going to cover it here!&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Save the state
&lt;/h4&gt;

&lt;p&gt;Create a file named &lt;code&gt;src/localStorage.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const loadState = () =&amp;gt; {
    try {
        const serializedState = localStorage.getItem("state");
        if (serializedState === null) {
            return undefined;
        }
        return JSON.parse(serializedState);
    } catch (err) {
        return undefined;
    }
};

export const saveState = (state) =&amp;gt; {
    try {
        const serializedState = JSON.stringify(state);
        localStorage.setItem("state", serializedState);
    } catch (error) {
        console.log(error)
    }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update the &lt;code&gt;configureStore.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createBrowserHistory } from "history";
import { applyMiddleware, compose, createStore } from "redux";
import { routerMiddleware } from "connected-react-router";
import createSagaMiddleware from "redux-saga";
import throttle from "lodash/throttle"; // Add lodash - we will need to throttle the saving
import { loadState, saveState } from "./localStorage"; // Import the load and save

import createRootReducer from "./redux/reducers";
import rootSaga from "./redux/sagas";

const sagaMiddleware = createSagaMiddleware();
export const history = createBrowserHistory();

const persistedState = loadState(); // Get the state from the localstorage

// remove the initial State from the function
const configureStore = () =&amp;gt; {
    const enhancers = [];
    const __DEV__ = process.env.NODE_ENV !== "production";
    if ( __DEV__ ) {
        const { devToolsExtension } = window;
        if (typeof devToolsExtension === "function") {
            enhancers.push(devToolsExtension());
        }
    }

    const middleware = [routerMiddleware(history), sagaMiddleware];
    const rootReducer = createRootReducer(history);

    // replace initialState with persistedState so on boot the app will use the data that exists in the state
    const store = createStore(rootReducer, persistedState, compose(applyMiddleware(...middleware), ...enhancers));

    sagaMiddleware.run(rootSaga);

    // subscribe to store updates so we can action saves on change to state 
    store.subscribe(
        // throttle the save
        throttle(() =&amp;gt; {
            saveState({
                auth: store.getState().auth // Only save these sections of the auth
                // if we want to save other state we can add it in here
                // activities: store.getState().activities, etc!
            });
        }, 1000) // 1 per second max!
    );

    return store;
};

export default configureStore();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we also need to add &lt;code&gt;lodash&lt;/code&gt; to help throttling the saving or every single redux action would trigger it...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add lodash

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authenticate your app again... then reload and you should still see the logged in state! Hurrah! Now let's get some activities!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/articles/strava-react-redux-saga-part-2/"&gt;part 2 of "Strava API activity sync with Axios, React, Redux &amp;amp; Saga"&lt;/a&gt; we will get the activities and display them.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting user locale with JavaScript</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Tue, 24 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/getting-user-locale-with-javascript-5d7n</link>
      <guid>https://dev.to/designer023/getting-user-locale-with-javascript-5d7n</guid>
      <description>&lt;p&gt;To get a user's prefered locale we can query the &lt;code&gt;navigator.language&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const locale = navigator.language;
console.log(locale); // =&amp;gt; "fr"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To change your language in Chrome go to &lt;code&gt;chrome://settings/languages&lt;/code&gt; and add/edit the list of languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To get the full list of user languages we can use &lt;code&gt;navigator.languages&lt;/code&gt;. This returns an array of locales in order of preference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(navigator.languages); // =&amp;gt; ["fr", "en", "en-US"]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: The locale value from &lt;code&gt;navigator.language&lt;/code&gt; is just the first item from the array of locales;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you need this value on a server side JavaScript build such as Gatsby then remember that the &lt;code&gt;node.js&lt;/code&gt; environment has no concept of the &lt;code&gt;navigator&lt;/code&gt; so we need to check for it and specify a fallback when it doesn't exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const locale = (navigator &amp;amp;&amp;amp; navigator.language) || "en";
console.log(locale); // Client =&amp;gt; "fr"
console.log(locale); // Server =&amp;gt; "en"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use this preference to format content such as dates, currency and time for users to their local preferences.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Styling React apps - what are the options?</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Sun, 01 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/styling-react-apps-what-are-the-options-1l7a</link>
      <guid>https://dev.to/designer023/styling-react-apps-what-are-the-options-1l7a</guid>
      <description>&lt;p&gt;When building a React app there are a number of ways you can styles your components and layout. These range from plain CSS classes on elements through to fully integrated style libraries that handle building the styles and creating class names for you.&lt;/p&gt;

&lt;p&gt;This is by no means an exhaustive list, but the key areas are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline styles&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;Sass build and imported as CSS&lt;/li&gt;
&lt;li&gt;Sass classes imported as a module&lt;/li&gt;
&lt;li&gt;Libraries such as Tailwind which are entirely utility class based systems&lt;/li&gt;
&lt;li&gt;Libraries such as Bootstrap where utility’s and prebuilt components are available&lt;/li&gt;
&lt;li&gt;Component libraries like ReactBootstrap and MUI with fully built components and properties&lt;/li&gt;
&lt;li&gt;Style building libraries like Styled Components&lt;/li&gt;
&lt;li&gt;JavaScript based styles like JSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Inline styles
&lt;/h2&gt;

&lt;p&gt;One of the original styling mechanisms of the web. Styles are placed directly on an element to style it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div style="margin-top: 10px; margin-bottom: 20px;"&amp;gt;&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a React component we need to change a few things... First, the styles string becomes an object. Next, the properties need to be converted to the JavaScript versions - &lt;code&gt;margin-top&lt;/code&gt; becomes &lt;code&gt;marginTop&lt;/code&gt;. The rule of thumb here is that if there is a hyphen in the property name, it's removed and the next letter is capitalised. Finally the values must be either strings or numbers. You can however concatenate these together - &lt;code&gt;10 + "px"&lt;/code&gt; or &lt;code&gt;${myVar}px&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div style={{
        marginTop: 10 + "px",
        marginBottom: `${myVar}px`
    }}
/&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's easy to create styles that work for just one element and there is no risk of namespacing clashes - however duplication is an issue, as is the lack of &lt;code&gt;:hover&lt;/code&gt; or &lt;code&gt;@media&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS
&lt;/h2&gt;

&lt;p&gt;We all know CSS. As with normal HTML applying classes to React components is just as simple - Just add the class name to the &lt;code&gt;className&lt;/code&gt; prop of the element instead of &lt;code&gt;class&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* index.css */

.myClass {
    property: value;
}


import './index.css'; 

// ...

&amp;lt;div className="myClass" /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An advantage of plain CSS is that it is as simple as creating a &lt;code&gt;.css&lt;/code&gt; file and importing it (in the App root or main index). The styles can then be used everwhere in your App. However the disadvantage of this global style is that it could have unintended styling side effects, especially if multiple people are working on a big project.&lt;/p&gt;

&lt;p&gt;A convoluted example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;span { color: red; }

/* somewhere else */

div span { color: blue; }

/* somewhere else again */

div {
    color: green !important;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👆 All the text will be green!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sass build and imported as CSS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sass-lang.com/"&gt;Sass&lt;/a&gt; follows the same pattern to CSS, except that you need to update the app to handle &lt;code&gt;sass&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add node-sass


/* index.scss */

.myClass {
    property: value;
}


import './index.scss'; 

// ...

&amp;lt;div className="myClass" /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has the same advantages, plus the ability to nest code, create mixins (re-usable blocks), functions and split files up. It used to also have an advantage of variables, but &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS now has them too&lt;/a&gt;. It also has the same disadvantages!&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS and Sass classes imported as modules
&lt;/h2&gt;

&lt;p&gt;The biggest disadvantage of both CSS and Sass is the global scope. By using them as modules (supported in Create React App currently) we can completely remove this issue. In CRA you must have a file with &lt;code&gt;*.module.css&lt;/code&gt; or &lt;code&gt;*.module.scss&lt;/code&gt; as the name. This makes CRA treat the file as a module and it is scoped to just this component that imports and uses it. There is no setup needed, it works out of the box as long as the files are named correctly. You can still mix normal css or sass with the modular version, but it's best to use global scpoed CSS just to reset browser styles and establish the very base of the styles like default fonts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* index.module.css */

.myClass {
    property: value;
}

.myOtherClass {
    property: value;
}


import styles from './index.module.scss'; 

// ...

&amp;lt;div className={styles.myClass} /&amp;gt;
&amp;lt;div className={styles.myOtherClass} /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the component renders, the actual classname used is a unique hash so there is no risk of namespace clashes and style conflicts with other modules. Global styles can still affect the modules though!&lt;/p&gt;

&lt;p&gt;This convoluted example will mess up your module styles, but it would also do that to all other examples too!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* index.css - global css styles */
* {
    display: none !important; 
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Utility class based frameworks
&lt;/h2&gt;

&lt;p&gt;Up until now we have relied on our own styles to style our app. Now we can look at some other options. Utility class frameworks consist classes that just apply to single things like padding left, margin top or backround colour. It's up to the developer to use these Lego bricks to create more complex kits. There have been a number of these over the years, but I want to focus on Tailwind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailwind
&lt;/h3&gt;

&lt;p&gt;A prime example is &lt;a href="https://tailwindcss.com/"&gt;Tailwind&lt;/a&gt;. All the styles have all be abstracted away as classes that can be used in combination to build up more complex components.&lt;/p&gt;

&lt;p&gt;There is a little more to configure than some of the other options but once done there is little need to revisit the config.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add required packages for Tailwind &amp;amp; React
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add tailwindcss autoprefixer postcss postcss-cli

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create a postcss config for Tailwind
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// file: postcss.config.js
const tailwindcss = require("tailwindcss");

module.exports = {
    plugins: [tailwindcss("./tailwind.config.js"), require("autoprefixer")]
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Update the build &amp;amp; start scripts to create the index.css
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
"scripts": {
    "build:css": "postcss src/styles/index.css -o src/index.css",
    "watch:css": "postcss src/styles/index.css -o src/index.css -w",
    "start": "run-p start:react",
    "start:react": "npm run watch:css &amp;amp; react-scripts start",
    "build:react": "npm run build:css &amp;amp;&amp;amp; react-scripts build",
    "build": "npm run build:react",
    ...
  },
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create a config for Tailwind
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// tailwind.config.js
module.exports = {
    purge: ["./src/**/*.jsx"],
    darkMode: "media", // or 'media' or 'class'
    theme: {
        extend: {}
    },
    variants: {
        extend: {}
    },
    plugins: []
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Import the created CSS and use the classes
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "./index.css";

// ...
&amp;lt;div class="w-32 h-32 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto"/&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the config is setup, then it's very similar to just using CSS (or Sass), just with a massive amount of prebuilt classes to utilise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component frameworks
&lt;/h2&gt;

&lt;p&gt;Some frameworks have extra patterns pre-built for developers to use. They can include forms, media, or popup, prebuilt so a single class or set of classes can setup a component quickly. Bootstrap is probably the most famous, although there are others like Bulma or Foundation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bootstrap
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; is similar to Tailwind in many respects. It's a library that gives a huge amount of customisation to the developer. Although not as wide ranging as Tailwind with it's utility classes, it makes up for with prebuild &lt;a href="https://getbootstrap.com/docs/5.0/customize/components/"&gt;component classes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add bootstrap


import 'bootstrap/dist/css/bootstrap.min.css';

// ...

&amp;lt;div className="mt-2 bg-danger text-white" /&amp;gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Component based libraries
&lt;/h2&gt;

&lt;p&gt;As well as libraries, such as Bootstrap, that contain the classes to build the components, there are also libraries that consist of prebuilt components. With these there is no need to add classes. The components already contain the styles needed to display correctly, and usually have props to alter it's variant.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Bootstrap
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://react-bootstrap.github.io/"&gt;React Bootstrap&lt;/a&gt; is a replacement for using BootStrap classes directly on elements. eg a &lt;code&gt;&amp;lt;h1 /&amp;gt;&lt;/code&gt; is replaced with the &lt;code&gt;&amp;lt;H1 /&amp;gt;&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add react-bootstrap bootstrap


import 'bootstrap/dist/css/bootstrap.min.css';
import Button from 'react-bootstrap/Button';

// ...
&amp;lt;Button variant="primary"&amp;gt;Hello&amp;lt;/Button&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Themes and customization can still be carried out by importing the bootstrap sass into your own &lt;code&gt;sass&lt;/code&gt; file and using it like the &lt;code&gt;sass/css&lt;/code&gt; options above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Material UI, aka. MUI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://material-ui.com/"&gt;Material UI&lt;/a&gt; is very similar to React Bootstrap in some ways. Unlike React Bootstrap though there is no CSS to import, but it contains a vast array of prebuilt components and layout wrappers. The configuration for MUI is done in JavaScript and not by overriding the Bootstrap theme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @material-ui/core


import { Button } from '@material-ui/core';

// ...

&amp;lt;Button color="primary"&amp;gt;Hello!&amp;lt;/Button&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To &lt;a href="https://material-ui.com/customization/theming/"&gt;theme Material UI&lt;/a&gt; there are a few handy utils like &lt;code&gt;createMuiTheme&lt;/code&gt; which alows customisation of core variables like Palette, Spacing, Typography; and and a &lt;code&gt;ThemeProvider&lt;/code&gt; context that allows the passing of the theme to child components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createMuiTheme } from '@material-ui/core/styles';

const theme = createMuiTheme({
  palette: {
    primary: {
      main: "#f00",
    }
  },
});

&amp;lt;ThemeProvider theme={theme}&amp;gt;
  &amp;lt;Button color="primary"&amp;gt;Hello!&amp;lt;/Button&amp;gt;
&amp;lt;/ThemeProvider&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Themes can also be nested to allow customisation of a subset of components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style building libraries
&lt;/h2&gt;

&lt;p&gt;These contain the raw building blocks for HTML markup and create a wrapper for creating scoped styled components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://styled-components.com/"&gt;Styled Components&lt;/a&gt; is the main example of this and is used under the hood by some other libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add styled-components

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Styled components is very simple to get up and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import styled from 'styled-components'

const Button = styled.button`
    background: #f00;
`

// ...

&amp;lt;Button&amp;gt;Hello&amp;lt;/Button&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we created a Button component that uses the styled component's &lt;code&gt;styled.button&lt;/code&gt; which returns a HTML button element. We then use regular CSS to set the background color between the `&lt;code&gt;&lt;/code&gt; `. Variables can also be used in styled components like so:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;br&gt;
import styled from 'styled-components'&lt;/p&gt;

&lt;p&gt;const Button = styled.button&lt;code&gt;&lt;br&gt;
    background: ${(props) =&amp;gt; props.background };&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;// ...&lt;/p&gt;

&lt;p&gt;Hello&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;This will use the &lt;code&gt;background&lt;/code&gt; prop passed to the component, in this case &lt;code&gt;#ff0&lt;/code&gt;. Where you are doing this, it's advisable to use &lt;code&gt;propTypes&lt;/code&gt; or similar to set default and required props so there is always a value to use!&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript based styles
&lt;/h2&gt;

&lt;p&gt;Who needs CSS anyway! Let's just use JavaScript for everything!&lt;/p&gt;

&lt;h3&gt;
  
  
  JSS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://cssinjs.org/"&gt;JSS&lt;/a&gt; is&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;br&gt;
yarn add react-jss&lt;/p&gt;

&lt;p&gt;import {createUseStyles} from 'react-jss'&lt;/p&gt;

&lt;p&gt;// ...&lt;/p&gt;

&lt;p&gt;// Create a style&lt;br&gt;
const useStyles = createUseStyles({&lt;br&gt;
  myClass: {&lt;br&gt;
    color: '#f0f'&lt;br&gt;
  },&lt;br&gt;
  // ...&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;const Button = ({children}) =&amp;gt; {&lt;br&gt;
    // Get the styles we created&lt;br&gt;
    const classes = useStyles()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Use them on the button
return (
    &amp;lt;button className={classes.myClass}&amp;gt;
        {children}
    &amp;lt;/button&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;const App = () =&amp;gt; (&lt;br&gt;
    Hello&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;As per MUI there is also the ability to theme;&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;br&gt;
import { createUseStyles, ThemeProvider, useTheme } from 'react-jss'&lt;/p&gt;

&lt;p&gt;// ...&lt;/p&gt;

&lt;p&gt;// Create a style&lt;br&gt;
const useStyles = createUseStyles({&lt;br&gt;
  myClass: {&lt;br&gt;
    color: '#f0f',&lt;br&gt;
    background: ({ theme }) =&amp;gt; theme.background&lt;br&gt;
  },&lt;br&gt;
  // ...&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;const Button = ({children}) =&amp;gt; {&lt;br&gt;
    // Get the styles we created&lt;br&gt;
    const classes = useStyles()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Use them on the button
return (
    &amp;lt;button className={classes.myClass}&amp;gt;
        {children}
    &amp;lt;/button&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;const theme = {&lt;br&gt;
  background: "#00f"&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const App = () =&amp;gt; (&lt;br&gt;
 &lt;br&gt;
    Hello&lt;br&gt;
  &lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was never a competition with a winner. All of the options have merits, caveats, pros and cons. I have found that, in my usage at least, each is better suited to different tasks.&lt;/p&gt;

&lt;p&gt;BootStrap, React BootStrap and MUI have it all built in and ready to go. You don't need to think about how to build a component, they already exist.&lt;/p&gt;

&lt;p&gt;CSS, Sass, JSS and Styled Components are more suited to building the UI from scratch.&lt;/p&gt;

&lt;p&gt;Tailwind sits roughly in the middle, with enough wiggle room to have your own styles but still have all the utilities to get something nice looking rapidly.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Getting to grips with CSS variables</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Wed, 21 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/getting-to-grips-with-css-variables-fgf</link>
      <guid>https://dev.to/designer023/getting-to-grips-with-css-variables-fgf</guid>
      <description>&lt;p&gt;If you have ever used JavaScript variables then the concept of CSS variables should feel pretty familiar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You set a variable&lt;/li&gt;
&lt;li&gt;You use the variable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CSS variables follows this same pattern, but the language is slightly different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
    --my-variable: red;
}

h1 {
    color: var(--my-variable);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is happening here? Firstly we set the variables - essentially they are custom properties. We use &lt;code&gt;--&lt;/code&gt; before the variable name to make it a variable. Then we use the variable by using &lt;code&gt;var(--variable-name)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;body&amp;gt; &amp;lt;!-- --my-variable: red; --&amp;gt;
    &amp;lt;h1&amp;gt;CSS - Carl's Super Styles!&amp;lt;/h1&amp;gt; &amp;lt;!-- colour: red --&amp;gt;
&amp;lt;/body&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usually most variables are set on the pseudo element &lt;code&gt;::root&lt;/code&gt; so that every HTML element will be encompassed by it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --color: red;
    --size: 20px;
    --background: #222;
    --padding: 1em;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scope of variables
&lt;/h2&gt;

&lt;p&gt;CSS variables are scoped to their ancestors (parents, though grandparents to the root pseudo element).&lt;/p&gt;

&lt;p&gt;Variables can be changed by child elements, but only apply further downstream in the DOM. That means that variables set in different branches of the DOM are only applicable to that tree. A bit like genetics, you only share the same genes from shared ancestors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing a variable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --my-variable: red;
}

body {
    --my-variable: blue;
}

h1 {
    color: var(--my-variable);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the root color of red will be overridden by the &lt;code&gt;my-variable&lt;/code&gt; on the body, so the &lt;code&gt;h1&lt;/code&gt; ends up with blue text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- :root --my-variable: red; --&amp;gt;
&amp;lt;body&amp;gt; &amp;lt;!-- --my-variable: blue; --&amp;gt;
    &amp;lt;h1&amp;gt;CSS - Carl's Super Styles!&amp;lt;/h1&amp;gt; &amp;lt;!-- colour: blue --&amp;gt;
&amp;lt;/body&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Same variable in different trees
&lt;/h3&gt;

&lt;p&gt;In the following example the root &lt;code&gt;--color&lt;/code&gt; variable is black. We then set the classes of &lt;code&gt;.red&lt;/code&gt; and &lt;code&gt;.blue&lt;/code&gt; to red and blue respectively. Then we create a &lt;code&gt;h2&lt;/code&gt; to consume the &lt;code&gt;--color&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --color: black;
}

.red {
    --color: red;
}

.blue {
    --color: blue;
}

h2 {
    color: var(--color);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;h2&lt;/code&gt; that is not wrapped by either one of &lt;code&gt;red&lt;/code&gt; or &lt;code&gt;blue&lt;/code&gt; inherits the value for the root color variable which is black. Both &lt;code&gt;red&lt;/code&gt; and &lt;code&gt;blue&lt;/code&gt; redefine the value of the variable and the h2 within their trees will be the relevant color.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h2&amp;gt;Black&amp;lt;/h2&amp;gt;

&amp;lt;div class="red"&amp;gt;
    &amp;lt;h2&amp;gt;Red&amp;lt;/h2&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class="blue"&amp;gt;
    &amp;lt;h2&amp;gt;Blue&amp;lt;/h2&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Media queries
&lt;/h2&gt;

&lt;p&gt;In the pre variable days of css we did this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.red { 
    font-size: 1em;
}

.blue { 
    font-size: 1em;
}

@media screen and (min-width: 32em) {
    .red {
         font-size: 2em;
     }
    .blue {
         font-size: 2em;
     }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With variables we can replace the hardcoded values with values set at the &lt;code&gt;:root&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --mobile-size: 1em;
    --desktop-size: 2em;
}

.red { 
    width: var(--mobile-size);
}

.blue { 
    width: var(--mobile-size);
}

@media screen and (min-width: 32em) {
    .red {
         width: var(--desktop-size);
     }
    .blue {
         width: var(--desktop-size);
     }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can do one better and move the media queries to the root, cutting out the need to add extra code for developers to maintain. Media queries will all be managed in 1 place too!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
    --font-size: 1em;
}

@media screen and (min-width: 32em) {
    :root {
        --font-size: 2em;
    }
}

.red { 
    width: var(--font-size);
}

.blue { 
    width: var(--font-size);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final notes and thoughts
&lt;/h2&gt;

&lt;p&gt;One of the important things to remember about CSS variables v1 is that the &lt;a href="https://www.w3.org/TR/css-variables-1/#using-variables"&gt;CSS spec&lt;/a&gt; states that &lt;code&gt;var&lt;/code&gt; can only be used to set properties, so you cannot use them as part of a media query.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;var()&lt;/code&gt; function can be used in place of any part of a value in any property on an element. The &lt;code&gt;var()&lt;/code&gt; function can not be used as property names, selectors, or anything else besides property values. (Doing so usually produces invalid syntax, or else a value whose meaning has no connection to the variable.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means that the following will currently not work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@media screen and (min-width: var(--min-width-desktop)) {
  /* ... the content */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is subject to change as later versions of the CSS spec are going to be rolled out in the future.&lt;/p&gt;

&lt;p&gt;Even without the current ability to use variables as media queries values, the extra control gained by having reusable values for regular CSS properties is invaluable. With the &lt;a href="https://caniuse.com/css-variables"&gt;vast majority of browsers supporting CSS variables&lt;/a&gt; then there really is no excuse not to use them in your daily workflow.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>React - Memo</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Fri, 24 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/react-memo-mae</link>
      <guid>https://dev.to/designer023/react-memo-mae</guid>
      <description>&lt;p&gt;Memoization is used to speed up applications by remembering the results of "expensive" function calls and calculations, so that they don't need to be recalculated again when the inputs have not changed.&lt;/p&gt;

&lt;p&gt;For example, if I ask you to calculate the results of a multiplication on paper or in your head, say &lt;code&gt;23 x 27&lt;/code&gt;, then I would expect that it would take you a little time to work out. Then, if I ask you to calculate it again with the same values (&lt;code&gt;23 x 27&lt;/code&gt;) would you calculate it again, or would you remember the previous answer and skip the calculation? You would re-use the previous result to save time. This is what memoization does.&lt;/p&gt;

&lt;p&gt;Of course in my example the maths is quick and easy for a computer and it's just meant to illustrate the concept. "Expensive" is just a way to describe things that take a longer time to be completed. Expensive for computers could be anything from complex maths, to iterating through large arrays of data to derive an answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  An example memoization function
&lt;/h3&gt;

&lt;p&gt;If we take a regular function that does a slow calculation, then calling it multiple times will equal multiple long calculations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function myFunction(input) {
  // Do things that take 5 seconds
  // return the calculated value
  return output;
}

myFunction(x); // 5 seconds
myFunction(x); // 5 seconds
myFunction(x); // 5 seconds

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we memoize this function we can save time on later calculations. The first time we call the function it completes the long calculation and caches the result. Every time we call the function after the first result it accesses the cache and can skip the long function resulting in a quicker return value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function myMemoisedFunction(input) {
  // create a cache property on the function and initialise it if it's not already
  myMemoisedFunction.memoCache = myMemoisedFunction.memoCache || {};

  // Check the cache for our input values
  // If it has them, then we have doen this before
  if (myMemoisedFunction.memoCache[input]) {
    // return the already calculated value
    return myMemoisedFunction.memoCache[input]
  }

  // Otherwize caluclate the slow things
  // Do things that take 5 seconds
  // set the cache values for next time
  myMemoisedFunction.memoCache[input] = output;

  // return the calculated value
  return output;
}

myMemoisedFunction(x); // 5 seconds
myMemoisedFunction(x); // 0.01 seconds
myMemoisedFunction(x); // 0.01 seconds

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the higher order functions of &lt;code&gt;memo&lt;/code&gt; and &lt;code&gt;useMemo&lt;/code&gt; are used for in React.&lt;/p&gt;

&lt;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;To understand why it can improve the performance you need to understand a few things about React.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components re-render during their lifecycle.&lt;/li&gt;
&lt;li&gt;When a parent re-renders, all it's children and descendants also re-render.&lt;/li&gt;
&lt;li&gt;When a component re-renders, it will recalculate variables.&lt;/li&gt;
&lt;li&gt;Recalculating has an overhead.&lt;/li&gt;
&lt;li&gt;If the calculation is expensive it will take time, and the application will be less performant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To counteract this issue react has 2 functions that can be used to memoize variables and components. These are the &lt;code&gt;useMemo&lt;/code&gt; hook and the &lt;code&gt;memo&lt;/code&gt; function. Both are higher-order functions - namely are used to wrap other functions (and functional components) to cache the results of expensive calculations and renders.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;useMemo&lt;/code&gt; hook
&lt;/h3&gt;

&lt;p&gt;The purpose of &lt;code&gt;useMemo&lt;/code&gt; is to optimize variable recalculation so that expensively generated variables only need to be calculated once, or if an argument changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Even though &lt;code&gt;memo&lt;/code&gt; can speed up an application, it does have an overhead, so using it on everything could be detrimental. The use case for &lt;code&gt;useMemo&lt;/code&gt; is to cache the results of expensive functions and shouldn't be used on all calculations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;useMemo&lt;/code&gt; consists of 2 parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An anonymous function that takes our expensive calculation which want to memoize.&lt;/li&gt;
&lt;li&gt;A dependency array that lists the variables that would cause the result to change - so it will only recalculate if one of these changes.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useMemo( ()=&amp;gt; { /* expensive function */ }, [/* Array of dependencies */])

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const memoizedResults = useMemo( ()=&amp;gt; reallyReallyComplexAndSlowCalculation(x, y), [x, y])

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The dependency array is used to determine if and when to re run the function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the following contrived example the &lt;code&gt;a + b&lt;/code&gt; will only be run if &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const memoizedResults = useMemo(()=&amp;gt; { return a + b }, [a, b]);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The &lt;code&gt;memo&lt;/code&gt; function.
&lt;/h3&gt;

&lt;p&gt;Memoization can also be used for components, however you need to use the &lt;code&gt;memo&lt;/code&gt; function to wrap your component. This can either be done when you declare your component, or when you export it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MyComponent = React.memo(({a, b}) =&amp;gt; {
  // ... the component
})

// or 

const MyComponent = ({a, b}) =&amp;gt; {
  // ... the component
};

export default React.memo(MyComponent);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example component that renders the result of &lt;code&gt;a&lt;/code&gt; + &lt;code&gt;b&lt;/code&gt; as &lt;code&gt;c&lt;/code&gt;. In this example the component will only render the first time, and if either &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; change, otherwise it will use the cached rendered version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MyComponent = ({a, b}) =&amp;gt; {
    const c = a + b;

    return (
        &amp;lt;div&amp;gt;{a} + {b} = {c}&amp;lt;/div&amp;gt;
    )
}

// Memoize the component
export default React.memo(MyComponent);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;It is worth noting that if your component uses &lt;code&gt;useContext&lt;/code&gt; or &lt;code&gt;useState&lt;/code&gt; hooks as part of its implementation, then any changes to their values &lt;strong&gt;will&lt;/strong&gt; trigger a re-render.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the following example we have a &lt;code&gt;useState&lt;/code&gt; hook. Only if the &lt;code&gt;setCount&lt;/code&gt; is called by clicking the "More!" button will the count update and trigger a component re-render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MyComponent = ({a, b}) =&amp;gt; {

    const [count, setCount] = useState(0);

    const c = a + b + count;

    return (
        &amp;lt;div&amp;gt;
          &amp;lt;button onClick={() =&amp;gt; setCount(count++)}&amp;gt;More!&amp;lt;/button&amp;gt;
          &amp;lt;div&amp;gt;{a} + {b} + {count} = {c}&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

// Memoize the component
export default React.memo(MyComponent);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Considerations for using &lt;code&gt;memo&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It's always worth looking at the profiler to see which components are slow to render and work from there with your optimisation. Even then, there are a few considerations to when you should use memoization on components.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pure functional components
&lt;/h4&gt;

&lt;p&gt;Because the &lt;code&gt;memo&lt;/code&gt; function compares variables and caches the result, the &lt;code&gt;memo&lt;/code&gt; fucnction should only be used on pure components - ones that will always render the same results given the same props. This means that the memo function will render the component on the first pass and won't render it again unless the props change.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rendered often
&lt;/h4&gt;

&lt;p&gt;If your component is rendered often then it's worth considering using &lt;code&gt;memo&lt;/code&gt;. If it's not rendered often then the optimisation may be unnecessary.&lt;/p&gt;

&lt;h4&gt;
  
  
  Uses the same props
&lt;/h4&gt;

&lt;p&gt;If you have a component where the props provided to it are often the same then you can optimise with &lt;code&gt;memo&lt;/code&gt;. If the props change regularly then there is no sense in optimising since the re-render with the same values will never (or rarely) be used and caching the values is an unnecessary overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;memo&lt;/code&gt; should have an integral part in most complex React applications, however their usage should be considered carefully as there is an overhead to using them. Using the profiler to detect and measure your performance gains should be a key part of your development cycle.&lt;/p&gt;

&lt;p&gt;If you use both &lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;memo&lt;/code&gt; correctly then you will save your app from needless re-renders and you will have a more optimised and performant app.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hooking into Gatsby's navigation changes</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Sun, 19 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/hooking-into-gatsbys-navigation-changes-57jg</link>
      <guid>https://dev.to/designer023/hooking-into-gatsbys-navigation-changes-57jg</guid>
      <description>&lt;p&gt;On my site there is a toggling navigation menu. But when I toggle it open and then click a link it stays open. This is not right - it should close when you change location. Let's fix it using some really simple hooks in a few minutes. First lets see what we have to start with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Original basic version of my Nav component
import React, { useState } from "react";

const Nav = ({ nav }) =&amp;gt; {
    const [open, setOpen] = useState(false);

    return (
      &amp;lt;div&amp;gt;
       &amp;lt;button
          type="button"
          onClick={() =&amp;gt; {
              setOpen(!open);
          }}
      &amp;gt;
        Toggle
      &amp;lt;/button&amp;gt;
        &amp;lt;div className={`${open ? "show" : null}`}&amp;gt;
          &amp;lt;Link to="/"&amp;gt;Home&amp;lt;/Link&amp;gt;
          &amp;lt;Link to="/about/"&amp;gt;About&amp;lt;/Link&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will let the user toggle the nav and see the links by toggling &lt;code&gt;setOpen&lt;/code&gt;. They can then navigate to a new page, but because the &lt;code&gt;Nav&lt;/code&gt; exists higher up the nesting it never gets re-mounted so &lt;code&gt;open&lt;/code&gt; stays in the same state which is &lt;code&gt;true&lt;/code&gt; - which is open.&lt;/p&gt;

&lt;p&gt;We need to hook into navigation changes - these are from the router and (luckily) are in the context sent to the page. Let's pull out the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Now with some location awareness
import React, { useState } from "react";
import { useLocation } from "@reach/router"; // NEW

const Nav = ({ nav }) =&amp;gt; {
    const [open, setOpen] = useState(false);
    const location = useLocation(); // NEW

    return (
      &amp;lt;div&amp;gt;
       &amp;lt;button
          type="button"
          onClick={() =&amp;gt; {
              setOpen(!open);
          }}
      &amp;gt;
        Toggle
      &amp;lt;/button&amp;gt;
        &amp;lt;div className={`${open ? "show" : null}`}&amp;gt;
          &amp;lt;Link to="/"&amp;gt;Home&amp;lt;/Link&amp;gt;
          &amp;lt;Link to="/about/"&amp;gt;About&amp;lt;/Link&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component is now aware of the location, but had no lasting memory so it does not know if anything has changed. Luckily we can use a hook to track that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Now with some location awareness
import React, { useState, useRef } from "react"; // UPDATED
import { useLocation } from "@reach/router";

// New hook
const usePrevious = (value) =&amp;gt; {
    const ref = useRef();
    useEffect(() =&amp;gt; {
        ref.current = value;
    });
    return ref.current;
};

const Nav = ({ nav }) =&amp;gt; {
    const [open, setOpen] = useState(false);
    const location = useLocation(); 
    const prevLocation = usePrevious(location); // NEW

    return (
      &amp;lt;div&amp;gt;
       &amp;lt;button
          type="button"
          onClick={() =&amp;gt; {
              setOpen(!open);
          }}
      &amp;gt;
        Toggle
      &amp;lt;/button&amp;gt;
        &amp;lt;div className={`${open ? "show" : null}`}&amp;gt;
          &amp;lt;Link to="/"&amp;gt;Home&amp;lt;/Link&amp;gt;
          &amp;lt;Link to="/about/"&amp;gt;About&amp;lt;/Link&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the current and previous locations it's a simple step to compare them. To make sure this only happens when the location changes we will use a &lt;code&gt;useEffect()&lt;/code&gt; and set the inputs to &lt;code&gt;location&lt;/code&gt; and &lt;code&gt;prevLocation&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Now with some location awareness
import React, { useState, useRef, useEffect } from "react"; // UPDATED
import { useLocation } from "@reach/router";

const usePrevious = (value) =&amp;gt; {
    const ref = useRef();
    useEffect(() =&amp;gt; {
        ref.current = value;
    });
    return ref.current;
};

const Nav = ({ nav }) =&amp;gt; {
    const [open, setOpen] = useState(false);
    const location = useLocation(); 
    const prevLocation = usePrevious(location);

    // NEW
    useEffect(() =&amp;gt; {
        if (location !== prevLocation) {
            setOpen(false);
        }
    }, [location, prevLocation, setOpen]);

    return (
      &amp;lt;div&amp;gt;
       &amp;lt;button
          type="button"
          onClick={() =&amp;gt; {
              setOpen(!open);
          }}
      &amp;gt;
        Toggle
      &amp;lt;/button&amp;gt;
        &amp;lt;div className={`${open ? "show" : null}`}&amp;gt;
          &amp;lt;Link to="/"&amp;gt;Home&amp;lt;/Link&amp;gt;
          &amp;lt;Link to="/about/"&amp;gt;About&amp;lt;/Link&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the final update the &lt;code&gt;Nav&lt;/code&gt; will now "listen" to location changes. When it changes and the location does not equal the previous one we set the state to false, closing the toggled menu.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running WordPress on Docker</title>
      <dc:creator>Carl Topham</dc:creator>
      <pubDate>Tue, 14 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/designer023/running-wordpress-on-docker-13hg</link>
      <guid>https://dev.to/designer023/running-wordpress-on-docker-13hg</guid>
      <description>&lt;p&gt;WordPress runs on a variety of platforms, but last time I was developing I was using a MAMP stack - Mac Apache MySQL &amp;amp; PHP. It's been a while since I was working on WordPress sites and I no longer have MAMP installed on my machine, however I do have Docker running. Since I can create a LAMP stack (Linux... AMP) running on my Mac. This is a bit closer to what the site would be running on in the real world, plus I can deploy the Docker app to something like a DigitalOcean Droplet quickly.&lt;/p&gt;

&lt;p&gt;Let's start by creating a folder for this to contain all the configs and data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir wordpress-docker &amp;amp;&amp;amp; cd wordpress-docker

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add a Docker Compose file, which is used to define and run multiple containers easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch docker-compose.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to configure the file itself, so in your favorite editor open up &lt;code&gt;docker-compose.yml&lt;/code&gt;. A few things are configured in the Docker Compose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The version of Docker Compose to use&lt;/li&gt;
&lt;li&gt;The services (containers) we are configuring. These consist of the database and the code.&lt;/li&gt;
&lt;li&gt;The image for each container to use&lt;/li&gt;
&lt;li&gt;What ports to open and bind to the host (your machine). This allows each container to speak to the other when needed&lt;/li&gt;
&lt;li&gt;An env file, where variables like database passwords can be stored so they can be exempt from being committed to source&lt;/li&gt;
&lt;li&gt;And in the case of the &lt;code&gt;wp&lt;/code&gt; container a link to the database container so it can be accessed by name as &lt;code&gt;mysql&lt;/code&gt; since the &lt;code&gt;wordpress&lt;/code&gt; container image uses this to connect.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "2"
services:
  db:
    image: mariadb
    ports:
      - "8081:3306"
    env_file:
      - .env
  wp:
    image: wordpress
    volumes:
      - ./html/:/var/www/html
    ports:
      - "8080:80"
    env_file:
      - .env
    links:
      - db:mysql

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; files for the variables (You could also use 2 &lt;code&gt;.env&lt;/code&gt; files, one for each container). Note that both passwords MUST be the same!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; MYSQL_ROOT_PASSWORD=SuperSecretTellNobody!
 WORDPRESS_DB_PASSWORD=SuperSecretTellNobody!

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for config, now it's time to start your Docker containers. The following command will start to download and build the images and then boot them up. Running it with &lt;code&gt;-d&lt;/code&gt; runs it in the background once&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it's booted up and is ready to go you will see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting wordpress-docker_db_1 ... done
Starting wordpress-docker_wp_1 ... done

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your browser head over to &lt;code&gt;http://localhost:8080/&lt;/code&gt; and you'll be welcomed by the initial setup screens.&lt;/p&gt;

&lt;p&gt;If you take a look in your directory you'll have a &lt;code&gt;./html&lt;/code&gt; sub-directory that contains all your WordPress files just like a normal WordPress install.&lt;/p&gt;

&lt;p&gt;Note: You can stop docker by running &lt;code&gt;docker compose down&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
