Tag: React

Demonstrating Reusable React Components in a Form

Components are the building blocks of React applications. It’s almost impossible to build a React application and not make use of components. It’s widespread to the point that some third-party packages provide you with components you can use to integrate functionality into your application.

These third-party components tend to be reusable. The difference between them and components you probably have in your application has to do with specificity.

Here’s what I mean. Say you run a company that sells polo shirts. There are two things you can do:

  1. You can produce polos that already have a design on them, or
  2. You can have the buyer choose the design they want.

Some fundamental things will be consistent, like all polos should be short-sleeved. But the user can pick variations of the shirts, such as the color and size. A short-sleeve polo would make a good React component in that case: it’s the same item with different variations.

Now let’s say you’re working on a login form. Like polo shirts, the form has consistent characteristics, but instead of size and color variations, we’d be looking at input fields, a submit button, perhaps even a lost password link. This can be componentized and implemented with variations in the inputs, buttons, links, etc.

Input Element Example

Let’s look at it from the perspective of creating an input field for your form. Here is how a typical text input will look like as a React component:

class Form extends React.Component {   this.state = {     username: ''   }      handleChange = (event) => {     this.setSate({ username: event.target.value })   }    render() {     return (       <input         name="username         type={type}         placeholder="Enter username"         onChange={this.handleChange}         value={this.state.username}       />     )   } }

To make this input element reusable in other places and projects, we’ll have to extract it into its own component. Let’s call it FormInput.

const FormInput = ({   name,   type,   placeholder,   onChange,   className,   value,   error,   children,   label,   ...props }) => {      return (     <React.Fragment>       <label htmlFor={name}>{label}</label>       <input         name={name}         type={type}         placeholder={placeholder}         onChange={onChange}         value={value}         className={className}         style={error && {border: 'solid 1px red'}}       />       { error && <p>{ error }</p>}     </React.Fragment>   ) }  FormInput.defaultProps = {   type: "text",   className: "" }  FormInput.propTypes = {   name: PropTypes.string.isRequired,   type: PropTypes.string,   placeholder: PropTypes.string.isRequired,   type: PropTypes.oneOf(['text', 'number', 'password']),   className: PropTypes.string,   value: PropTypes.any,   onChange: PropTypes.func.isRequired }

The component accepts certain props, such as the attributes that we need to make the input with valid markup, including the placeholder, value and name. We set up the input element in the render function, setting the attribute values as the props that are passed to the component. We even bind the input to a label to ensure they’re always together. You can see that we’re not making assumptions by predefining anything. The idea is to ensure that the component can be used in as many scenarios as possible.

This makes for a good component because it enforces good markup (something Brad Frost calls dumb React) which goes to show that not every component has to be some highly complex piece of functionality. Then again, if we were talking about something super basic, say a static heading, then reaching for a React component might be overkill. The possible yardstick for making something a reusable component probably ought to be when you need the same functionality in other parts of an application. There’s generally no need for a “reusable” component if that component is only used once.

We can make use of our input component in another component, the LoginPage.

class LoginPage extends React.Component {   state = {     user: {       username: "",       password: ""     },     errors: {},     submitted: false   };    handleChange = event => {     const { user } = this.state;     user[event.target.name] = event.target.value;     this.setState({ user });   };    onSubmit = () => {     const {       user: { username, password }     } = this.state;     let err = {};      if (!username) {       err.username = "Enter your username!";     }      if (password.length < 8) {       err.password = "Password must be at least 8 characters!";     }      this.setState({ errors: err }, () => {       if (Object.getOwnPropertyNames(this.state.errors).length === 0) {         this.setState({ submitted: true });       }     });   };    render() {     const {       submitted,       errors,       user: { username, password }     } = this.state;     return (       <React.Fragment>         {submitted ? (           <p>Welcome onboard, {username}!</p>         ) : (           <React.Fragment>             <h3>Login!</h3>             <FormInput               label="Username"               name="username"               type="text"               value={username}               onChange={this.handleChange}               placeholder="Enter username..."               error={errors.username}               required               className="input"             />              <FormInput               label="Password"               name="password"               type="password"               value={password}               onChange={this.handleChange}               placeholder="Enter password..."               error={errors.password}               className="input"               required             />              <Button               type="submit"               label="Submit"               className="button"               handleClick={this.onSubmit}             />           </React.Fragment>         )}       </React.Fragment>     );   } }

See how LoginPage uses the FormInput twice? We’re using it both as the text input for a username and another text input for a password. If we want to make any changes to how the input functions, we can make those changes inside the FormInput component file we created and they will be applied to every instance where the input component is used. That’s the fundamental upside to having reusable components: you don’t have to repeat yourself.

Even the errors are displayed from the FormInput component.

The onSubmit function first validates the user object which we get from the form, ensuring that it fits the structure that there is a value for username. Notice that we can even extend the input’s functionality, like we did to check that the password contains at least eight characters.

If you look at the code, you’ll see a have a Button component in there. That’s not the same as a HTML <button> element, but instead another component that takes the props that define the type of button we want (submit, reset, button), its class name, what to do on click, and the label. There are lots of other button attributes we could integrate to enforce whatever standard is needed.

const Button = (props) => (   <button     type={props.type}     className={props.className}     onClick={props.handleClick}   >     {props.label}   </button> )

Here’s our final login form when all of our components are put together.

See the Pen
Reusable Button Component
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.


Wanna give this a try yourself? Try working on a reusable <select> element. If that’s too difficult, you can start with a <textarea> element, then perhaps a checkbox before hopping over to <select>. The key idea is to make it generic. I’ll like to see what you came up with, so link up your work in the comment section!

The post Demonstrating Reusable React Components in a Form appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

Thinking in React Hooks

Amelia Wattenberger has written this wonderful and interactive piece about React Hooks and details how they can clean up code and remove all those troubling lifecycle events:

React introduced hooks one year ago, and they’ve been a game-changer for a lot of developers. There are tons of how-to introduction resources out there, but I want to talk about the fundamental mindset change when switching from React class components to function components + hooks.

Make sure you check out that lovely animation when you select the code, too. It’s a pretty smart effect to show the old way of doing things versus the new and fancy way. Also make sure to check out our very own Intro to React Hooks once you’re finished to find more examples of this pattern in use.

Direct Link to ArticlePermalink

The post Thinking in React Hooks appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

A Dark Mode Toggle with React and ThemeProvider

I like when websites have a dark mode option. Dark mode makes web pages easier for me to read and helps my eyes feel more relaxed. Many websites, including YouTube and Twitter, have implemented it already, and we’re starting to see it trickle onto many other sites as well.

In this tutorial, we’re going to build a toggle that allows users to switch between light and dark modes, using a <ThemeProvider wrapper from the styled-components library. We’ll create a useDarkMode custom hook, which supports the prefers-color-scheme media query to set the mode according to the user’s OS color scheme settings.

If that sounds hard, I promise it’s not! Let’s dig in and make it happen.

See the Pen
Day/night mode switch toggle with React and ThemeProvider
by Maks Akymenko (@maximakymenko)
on CodePen.

Let’s set things up

We’ll use create-react-app to initiate a new project:

npx create-react-app my-app cd my-app yarn start

Next, open a separate terminal window and install styled-components:

yarn add styled-components

Next thing to do is create two files. The first is global.js, which will contain our base styling, and the second is theme.js, which will include variables for our dark and light themes:

// theme.js export const lightTheme = {   body: '#E2E2E2',   text: '#363537',   toggleBorder: '#FFF',   gradient: 'linear-gradient(#39598A, #79D7ED)', }  export const darkTheme = {   body: '#363537',   text: '#FAFAFA',   toggleBorder: '#6B8096',   gradient: 'linear-gradient(#091236, #1E215D)', }

Feel free to customize variables any way you want, because this code is used just for demonstration purposes.

// global.js // Source: https://github.com/maximakymenko/react-day-night-toggle-app/blob/master/src/global.js#L23-L41  import { createGlobalStyle } from 'styled-components';  export const GlobalStyles = createGlobalStyle`   *,   *::after,   *::before {     box-sizing: border-box;   }    body {     align-items: center;     background: $ {({ theme }) => theme.body};     color: $ {({ theme }) => theme.text};     display: flex;     flex-direction: column;     justify-content: center;     height: 100vh;     margin: 0;     padding: 0;     font-family: BlinkMacSystemFont, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;     transition: all 0.25s linear;   }

Go to the App.js file. We’re going to delete everything in there and add the layout for our app. Here’s what I did:

import React from 'react'; import { ThemeProvider } from 'styled-components'; import { lightTheme, darkTheme } from './theme'; import { GlobalStyles } from './global';  function App() {   return (     <ThemeProvider theme={lightTheme}>       <>         <GlobalStyles />         <button>Toggle theme</button>         <h1>It's a light theme!</h1>         <footer>         </footer>       </>     </ThemeProvider>   ); }  export default App;

This imports our light and dark themes. The ThemeProvider component also gets imported and is passed the light theme (lightTheme) styles inside. We also import GlobalStyles to tighten everything up in one place.

Here’s roughly what we have so far:

Now, the toggling functionality

There is no magic switching between themes yet, so let’s implement toggling functionality. We are only going to need a couple lines of code to make it work.

First, import the useState hook from react:

// App.js import React, { useState } from 'react';

Next, use the hook to create a local state which will keep track of the current theme and add a function to switch between themes on click:

// App.js const [theme, setTheme] = useState('light');  // The function that toggles between themes const toggleTheme = () => {   // if the theme is not light, then set it to dark   if (theme === 'light') {     setTheme('dark');   // otherwise, it should be light   } else {     setTheme('light');   } }

After that, all that’s left is to pass this function to our button element and conditionally change the theme. Take a look:

// App.js import React, { useState } from 'react'; import { ThemeProvider } from 'styled-components'; import { lightTheme, darkTheme } from './theme'; import { GlobalStyles } from './global';  // The function that toggles between themes function App() {   const [theme, setTheme] = useState('light');   const toggleTheme = () => {     if (theme === 'light') {       setTheme('dark');     } else {       setTheme('light');     }   }      // Return the layout based on the current theme   return (     <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>       <>         <GlobalStyles />         // Pass the toggle functionality to the button         <button onClick={toggleTheme}>Toggle theme</button>         <h1>It's a light theme!</h1>         <footer>         </footer>       </>     </ThemeProvider>   ); }  export default App;

How does it work?

// global.js background: $ {({ theme }) => theme.body}; color: $ {({ theme }) => theme.text}; transition: all 0.25s linear;

Earlier in our GlobalStyles, we assigned background and color properties to values from the theme object, so now, every time we switch the toggle, values change depending on the darkTheme and lightTheme objects that we are passing to ThemeProvider. The transition property allows us to make this change a little more smoothly than working with keyframe animations.

Now we need the toggle component

We’re generally done here because you now know how to create toggling functionality. However, we can always do better, so let’s improve the app by creating a custom Toggle component and make our switch functionality reusable. That’s one of the key benefits to making this in React, right?

We’ll keep everything inside one file for simplicity’s sake,, so let’s create a new one called Toggle.js and add the following:

// Toggle.js import React from 'react' import { func, string } from 'prop-types'; import styled from 'styled-components'; // Import a couple of SVG files we'll use in the design: https://www.flaticon.com import { ReactComponent as MoonIcon } from 'icons/moon.svg'; import { ReactComponent as SunIcon } from 'icons/sun.svg';  const Toggle = ({ theme, toggleTheme }) => {   const isLight = theme === 'light';   return (     <button onClick={toggleTheme} >       <SunIcon />       <MoonIcon />     </button>   ); };  Toggle.propTypes = {   theme: string.isRequired,   toggleTheme: func.isRequired, }  export default Toggle;

You can download icons from here and here. Also, if we want to use icons as components, remember about importing them as React components.

We passed two props inside: the theme will provide the current theme (light or dark) and toggleTheme function will be used to switch between them. Below we created an isLight variable, which will return a boolean value depending on our current theme. We’ll pass it later to our styled component.

We’ve also imported a styled function from styled-components, so let’s use it. Feel free to add this on top your file after the imports or create a dedicated file for that (e.g. Toggle.styled.js) like I have below. Again, this is purely for presentation purposes, so you can style your component as you see fit.

// Toggle.styled.js const ToggleContainer = styled.button`   background: $ {({ theme }) => theme.gradient};   border: 2px solid $ {({ theme }) => theme.toggleBorder};   border-radius: 30px;   cursor: pointer;   display: flex;   font-size: 0.5rem;   justify-content: space-between;   margin: 0 auto;   overflow: hidden;   padding: 0.5rem;   position: relative;   width: 8rem;   height: 4rem;    svg {     height: auto;     width: 2.5rem;     transition: all 0.3s linear;          // sun icon     &:first-child {       transform: $ {({ lightTheme }) => lightTheme ? 'translateY(0)' : 'translateY(100px)'};     }          // moon icon     &:nth-child(2) {       transform: $ {({ lightTheme }) => lightTheme ? 'translateY(-100px)' : 'translateY(0)'};     }   } `;

Importing icons as components allows us to directly change the styles of the SVG icons. We’re checking if the lightTheme is an active one, and if so, we move the appropriate icon out of the visible area — sort of like the moon going away when it’s daytime and vice versa.

Don’t forget to replace the button with the ToggleContainer component in Toggle.js, regardless of whether you’re styling in separate file or directly in Toggle.js. Be sure to pass the isLight variable to it to specify the current theme. I called the prop lightTheme so it would clearly reflect its purpose.

The last thing to do is import our component inside App.js and pass required props to it. Also, to add a bit more interactivity, I’ve passed condition to toggle between “light” and “dark” in the heading when the theme changes:

// App.js <Toggle theme={theme} toggleTheme={toggleTheme} /> <h1>It's a {theme === 'light' ? 'light theme' : 'dark theme'}!</h1>

Don’t forget to credit the flaticon.com authors for the providing the icons.

// App.js <span>Credits:</span> <small><b>Sun</b> icon made by <a href="https://www.flaticon.com/authors/smalllikeart">smalllikeart</a> from <a href="https://www.flaticon.com">www.flaticon.com</a></small> <small><b>Moon</b> icon made by <a href="https://www.freepik.com/home">Freepik</a> from <a href="https://www.flaticon.com">www.flaticon.com</a></small>

Now that’s better:

The useDarkMode hook

While building an application, we should keep in mind that the app must be scalable, meaning, reusable, so we can use in it many places, or even different projects.

That is why it would be great if we move our toggle functionality to a separate place — so, why not to create a dedicated account hook for that?

Let’s create a new file called useDarkMode.js in the project src directory and move our logic into this file with some tweaks:

// useDarkMode.js import { useEffect, useState } from 'react';  export const useDarkMode = () => {   const [theme, setTheme] = useState('light');   const toggleTheme = () => {     if (theme === 'light') {       window.localStorage.setItem('theme', 'dark')       setTheme('dark')     } else {       window.localStorage.setItem('theme', 'light')       setTheme('light')     }   };    useEffect(() => {     const localTheme = window.localStorage.getItem('theme');     localTheme && setTheme(localTheme);   }, []);    return [theme, toggleTheme] };

We’ve added a couple of things here. We want our theme to persist between sessions in the browser, so if someone has chosen a dark theme, that’s what they’ll get on the next visit to the app. That’s a huge UX improvement. For this reasons we use localStorage.

We’ve also implemented the useEffect hook to check on component mounting. If the user has previously selected a theme, we will pass it to our setTheme function. In the end, we will return our theme, which contains the chosen theme and toggleTheme function to switch between modes.

Now, let’s implement the useDarkMode hook. Go into App.js, import the newly created hook, destructure our theme and toggleTheme properties from the hook, and, put them where they belong:

// App.js import React from 'react'; import { ThemeProvider } from 'styled-components'; import { useDarkMode } from './useDarkMode'; import { lightTheme, darkTheme } from './theme'; import { GlobalStyles } from './global'; import Toggle from './components/Toggle';  function App() {   const [theme, toggleTheme] = useDarkMode();   const themeMode = theme === 'light' ? lightTheme : darkTheme;    return (     <ThemeProvider theme={themeMode}>       <>         <GlobalStyles />         <Toggle theme={theme} toggleTheme={toggleTheme} />         <h1>It's a {theme === 'light' ? 'light theme' : 'dark theme'}!</h1>         <footer>           Credits:           <small>Sun icon made by smalllikeart from www.flaticon.com</small>           <small>Moon icon made by Freepik from www.flaticon.com</small>         </footer>       </>     </ThemeProvider>   ); }  export default App;

This almost works almost perfectly, but there is one small thing we can do to make our experience better. Switch to dark theme and reload the page. Do you see that the sun icon loads before the moon icon for a brief moment?

That happens because our useState hook initiates the light theme initially. After that, useEffect runs, checks localStorage and only then sets the theme to dark.

So far, I found two solutions. The first is to check if there is a value in localStorage in our useState:

// useDarkMode.js const [theme, setTheme] = useState(window.localStorage.getItem('theme') || 'light');

However, I am not sure if it’s a good practice to do checks like that inside useState, so let me show you a second solution, that I’m using.

This one will be a bit more complicated. We will create another state and call it componentMounted. Then, inside the useEffect hook, where we check our localTheme, we’ll add an else statement, and if there is no theme in localStorage, we’ll add it. After that, we’ll set setComponentMounted to true. In the end, we add componentMounted to our return statement.

// useDarkMode.js import { useEffect, useState } from 'react';  export const useDarkMode = () => {   const [theme, setTheme] = useState('light');   const [componentMounted, setComponentMounted] = useState(false);   const toggleTheme = () => {     if (theme === 'light') {       window.localStorage.setItem('theme', 'dark');       setTheme('dark');     } else {       window.localStorage.setItem('theme', 'light');       setTheme('light');     }   };    useEffect(() => {     const localTheme = window.localStorage.getItem('theme');     if (localTheme) {       setTheme(localTheme);     } else {       setTheme('light')       window.localStorage.setItem('theme', 'light')     }     setComponentMounted(true);   }, []);      return [theme, toggleTheme, componentMounted] };

You might have noticed that we’ve got some pieces of code that are repeated. We always try to follow the DRY principle while writing the code, and right here we’ve got a chance to use it. We can create a separate function that will set our state and pass theme to the localStorage. I believe, that the best name for it will be setTheme, but we’ve already used it, so let’s call it setMode:

// useDarkMode.js const setMode = mode => {   window.localStorage.setItem('theme', mode)   setTheme(mode) };

With this function in place, we can refactor our useDarkMode.js a little:

// useDarkMode.js import { useEffect, useState } from 'react'; export const useDarkMode = () => {   const [theme, setTheme] = useState('light');   const [componentMounted, setComponentMounted] = useState(false);    const setMode = mode => {     window.localStorage.setItem('theme', mode)     setTheme(mode)   };    const toggleTheme = () => {     if (theme === 'light') {       setMode('dark');     } else {       setMode('light');     }   };    useEffect(() => {     const localTheme = window.localStorage.getItem('theme');     if (localTheme) {       setTheme(localTheme);     } else {       setMode('light');     }     setComponentMounted(true);   }, []);    return [theme, toggleTheme, componentMounted] };

We’ve only changed code a little, but it looks so much better and is easier to read and understand!

Did the component mount?

Getting back to componentMounted property. We will use it to check if our component has mounted because this is what happens in useEffect hook.

If it hasn’t happened yet, we will render an empty div:

// App.js if (!componentMounted) {   return <div /> };

Here is how complete code for the App.js:

// App.js import React from 'react'; import { ThemeProvider } from 'styled-components'; import { useDarkMode } from './useDarkMode'; import { lightTheme, darkTheme } from './theme'; import { GlobalStyles } from './global'; import Toggle from './components/Toggle';  function App() {   const [theme, toggleTheme, componentMounted] = useDarkMode();    const themeMode = theme === 'light' ? lightTheme : darkTheme;    if (!componentMounted) {     return <div />   };    return (     <ThemeProvider theme={themeMode}>       <>         <GlobalStyles />         <Toggle theme={theme} toggleTheme={toggleTheme} />         <h1>It's a {theme === 'light' ? 'light theme' : 'dark theme'}!</h1>         <footer>           <span>Credits:</span>           <small><b>Sun</b> icon made by <a href="https://www.flaticon.com/authors/smalllikeart">smalllikeart</a> from <a href="https://www.flaticon.com">www.flaticon.com</a></small>           <small><b>Moon</b> icon made by <a href="https://www.freepik.com/home">Freepik</a> from <a href="https://www.flaticon.com">www.flaticon.com</a></small>         </footer>       </>     </ThemeProvider>   ); }  export default App;

Using the user’s preferred color scheme

This part is not required, but it will let you achieve even better user experience. This media feature is used to detect if the user has requested the page to use a light or dark color theme based on the settings in their OS. For example, if a user’s default color scheme on a phone or laptop is set to dark, your website will change its color scheme accordingly to it. It’s worth noting that this media query is still a work in progress and is included in the Media Queries Level 5 specification, which is in Editor’s Draft.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
76 62 67 No 76 12.1

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
13 No No 76 No 68

The implementation is pretty straightforward. Because we’re working with a media query, we need to check if the browser supports it in the useEffect hook and set appropriate theme. To do that, we’ll use window.matchMedia to check if it exists and whether dark mode is supported. We also need to remember about the localTheme because, if it’s available, we don’t want to overwrite it with the dark value unless, of course, the value is set to light.

If all checks are passed, we will set the dark theme.

// useDarkMode.js useEffect(() => { if (   window.matchMedia &&   window.matchMedia('(prefers-color-scheme: dark)').matches &&    !localTheme ) {   setTheme('dark')   } })

As mentioned before, we need to remember about the existence of localTheme — that’s why we need to implement our previous logic where we’ve checked for it.

Here’s what we had from before:

// useDarkMode.js useEffect(() => { const localTheme = window.localStorage.getItem('theme');   if (localTheme) {     setTheme(localTheme);   } else {     setMode('light');   } })

Let’s mix it up. I’ve replaced the if and else statements with ternary operators to make things a little more readable as well:

// useDarkMode.js useEffect(() => { const localTheme = window.localStorage.getItem('theme'); window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && !localTheme ?   setMode('dark') :   localTheme ?     setTheme(localTheme) :     setMode('light');}) })

Here’s the userDarkMode.js file with the complete code:

// useDarkMode.js import { useEffect, useState } from 'react';  export const useDarkMode = () => {   const [theme, setTheme] = useState('light');   const [componentMounted, setComponentMounted] = useState(false);   const setMode = mode => {     window.localStorage.setItem('theme', mode)     setTheme(mode)   };    const toggleTheme = () => {     if (theme === 'light') {       setMode('dark')     } else {       setMode('light')     }   };    useEffect(() => {     const localTheme = window.localStorage.getItem('theme');     window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && !localTheme ?       setMode('dark') :       localTheme ?         setTheme(localTheme) :         setMode('light');     setComponentMounted(true);   }, []);    return [theme, toggleTheme, componentMounted] };

Give it a try! It changes the mode, persists the theme in localStorage, and also sets the default theme accordingly to the OS color scheme if it’s available.


Congratulations, my friend! Great job! If you have any questions about implementation, feel free to send me a message!

The post A Dark Mode Toggle with React and ThemeProvider appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Filtering Data Client-Side: Comparing CSS, jQuery, and React

Say you have a list of 100 names:

<ul>   <li>Randy Hilpert</li>   <li>Peggie Jacobi</li>   <li>Ethelyn Nolan Sr.</li>    <!-- and then some --> </ul>

…or file names, or phone numbers, or whatever. And you want to filter them client-side, meaning you aren’t making a server-side request to search through data and return results. You just want to type “rand” and have it filter the list to include “Randy Hilpert” and “Danika Randall” because they both have that string of characters in them. Everything else isn’t included in the results.

Let’s look at how we might do that with different technologies.

CSS can sorta do it, with a little help.

CSS can’t select things based on the content they contain, but it can select on attributes and the values of those attributes. So let’s move the names into attributes as well.

<ul>   <li data-name="Randy Hilpert">Randy Hilpert</li>   <li data-name="Peggie Jacobi">Peggie Jacobi</li>   <li data-name="Ethelyn Nolan Sr.">Ethelyn Nolan Sr.</li>    ... </ul>

Now to filter that list for names that contain “rand”, it’s very easy:

li {   display: none; } li[data-name*="rand" i] {   display: list-item; }

Note the i on Line 4. That means “case insensitive” which is very useful here.

To make this work dynamically with a filter <input>, we’ll need to get JavaScript involved to not only react to the filter being typed in, but generate CSS that matches what is being searched.

Say we have a <style> block sitting on the page:

<style id="cssFilter">   /* dynamically generated CSS will be put in here */ </style>

We can watch for changes on our filter input and generate that CSS:

filterElement.addEventListener("input", e => {   let filter = e.target.value;   let css = filter ? `     li {       display: none;     }     li[data-name*="$  {filter}" i] {       display: list-item;     }   ` : ``;   window.cssFilter.innerHTML = css; });

Note that we’re emptying out the style block when the filter is empty, so all results show.

See the Pen
Filtering Technique: CSS
by Chris Coyier (@chriscoyier)
on CodePen.

I’ll admit it’s a smidge weird to leverage CSS for this, but Tim Carry once took it way further if you’re interested in the concept.

jQuery makes it even easier.

Since we need JavaScript anyway, perhaps jQuery is an acceptable tool. There are two notable changes here:

  • jQuery can select items based on the content they contain. It has a selector API just for this. We don’t need the extra attribute anymore.
  • This keeps all the filtering to a single technology.

We still watch the input for typing, then if we have a filter term, we hide all the list items and reveal the ones that contain our filter term. Otherwise, we reveal them all again:

const listItems = $  ("li");  $  ("#filter").on("input", function() {   let filter = $  (this).val();   if (filter) {     listItems.hide();     $  (`li:contains('$  {filter}')`).show();   } else {     listItems.show();   } });

It’s takes more fiddling to make the filter case-insensitive than CSS does, but we can do it by overriding the default method:

jQuery.expr[':'].contains = function(a, i, m) {   return jQuery(a).text().toUpperCase()       .indexOf(m[3].toUpperCase()) >= 0; };

See the Pen
Filtering Technique: jQuery
by Chris Coyier (@chriscoyier)
on CodePen.

React can do it with state and rendering only what it needs.

There is no one-true-way to do this in React, but I would think it’s React-y to keep the list of names as data (like an Array), map over them, and only render what you need. Changes in the input filter the data itself and React re-renders as necessary.

If we have an names = [array, of, names], we can filter it pretty easily:

filteredNames = names.filter(name => {   return name.includes(filter); });

This time, case sensitivity can be done like this:

filteredNames = names.filter(name => {   return name.toUpperCase().includes(filter.toUpperCase()); });

Then we’d do the typical .map() thing in JSX to loop over our array and output the names.

See the Pen
Filtering Technique: React
by Chris Coyier (@chriscoyier)
on CodePen.

I don’t have any particular preference

This isn’t the kind of thing you choose a technology for. You do it in whatever technology you already have. I also don’t think any one approach is particularly heavier than the rest in terms of technical debt.

The post Filtering Data Client-Side: Comparing CSS, jQuery, and React appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Using Immer for React State Management

We make use of state to keep track of application data. States change as users interact with an application. When this happens, we need to update the state that is displayed to the user, and we do this using React’s setState.

Since states are not meant to be updated directly (because React’s state has to be immutable), things can get really complicated as states become more complex. They become difficult to understand and follow.

This is where Immer comes in and that’s what we’re going to look at in this post. Using Immer, states can be simplified and much easier to follow. Immer makes use of something called “draft” which you can think of as the copy of your state, but not the state itself. It’s as though Immer hit CMD+C on the state and then cmd+V’d it somewhere else where it can be safely viewed without disturbing the original copy. Any updates you need to make happen on the draft, and the parts of the current state that change on the draft is updated.

Let’s say your application’s state looks like this;

this.state = {   name: 'Kunle',   age: 30,   city: 'Lagos,   country: 'Nigeria' }

This user happens to be celebrating his 31st birthday and which means we need to update the age value. With Immer running behind the scenes, a replica of this state will be made.

Now imagine the replica is made and handed over to a messenger, who gives the newly copied version of the state to Kunle. It means there are now two copies available — the current state and the draft copy that was handed over. Kunle then changes the age on the draft to 31. The messenger then returns to the application with the draft, compares both versions, and only updates the age since that’s the only part of the draft that changed.

It does not break the idea of an immutable state, as the current state does not get updated directly. Immer basically makes it convenient to work with immutable state.

Let’s look at an example of this at work

Say you want to build a traffic light for your community, you can give it a shot using Immer for your state updates.

See the Pen
Traffic Light Example with Reactjs
by CarterTsai (@CarterTsai)
on CodePen.

Using Immer, the component will look like this:

const {produce} = immer  class App extends React.Component {   state = {     red: 'red',      yellow: 'black',      green: 'black',     next: "yellow"   }    componentDidMount() {     this.interval = setInterval(() => this.changeHandle(), 3000);   }      componentWillUnmount()  {     clearInterval(this.interval);   }    handleRedLight = () => {     this.setState(       produce(draft => {         draft.red = 'red';         draft.yellow = 'black';         draft.green = 'black';         draft.next = 'yellow'       })     )   }      handleYellowLight = () => {     this.setState(       produce(draft => {         draft.red = 'black';         draft.yellow = 'yellow';         draft.green = 'black';         draft.next = 'green'       })     )   }      handleGreenLight = () => {     this.setState(       produce(draft => {         draft.red = 'black';         draft.yellow = 'black';         draft.green = 'green';         draft.next = 'red'       })     )   }    changeHandle = () => {     if (this.state.next === 'yellow') {       this.handleYellowLight()     } else if (this.state.next === 'green') {       this.handleGreenLight()     } else {       this.handleRedLight()     }        }    render() {     return (       <div className="box">         <div className="circle" style={{backgroundColor: this.state.red}}></div>         <div className="circle" style={{backgroundColor: this.state.yellow}}></div>         <div className="circle" style={{backgroundColor: this.state.green}}></div>       </div>   ); } };

produce is the default function we get from Immer. Here, we pass it as a value to the setState() method. The produce function takes a function which accepts draft as an argument. It is inside this function that we can then set the draft copy with which we want to update our state.

If that looks complicated, there is another way to write this. First, we create a function.

const handleLight = (state) => {   return produce(state, (draft) => {     draft.red = 'black';     draft.yellow = 'black';     draft.green = 'green';     draft.next = 'red'   }); }

We are passing the current state of the application, and the function which accepts draft as arguments to the produce function. To make use of this inside our component, we do this;

handleGreenLight = () => {   const nextState = handleLight(this.state)   this.setState(nextState) }

Another example: A shopping list

If you have been working with React for a while now, then you’re not a stranger to the spread operator. With Immer, you need not make use of the spread operator, especially when working with an array in your state.

Let’s explore that a little further by creating a shopping list application.

See the Pen
immer 2 – shopping list
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

Here’s the component we’re working with:

class App extends React.Component {   constructor(props) {       super(props)              this.state = {         item: "",         price: 0,         list: [           { id: 1, name: "Cereals", price: 12 },           { id: 2, name: "Rice", price: 10 }         ]       }     }      handleInputChange = e => {       this.setState(       produce(draft => {         draft[event.target.name] = event.target.value       }))     }      handleSubmit = (e) => {       e.preventDefault()       const newItem = {         id: uuid.v4(),         name: this.state.name,         price: this.state.price       }       this.setState(         produce(draft => {           draft.list = draft.list.concat(newItem)         })       )     };    render() {     return (       <React.Fragment>         <section className="section">           <div className="box">             <form onSubmit={this.handleSubmit}>               <h2>Create your shopping list</h2>               <div>                 <input                   type="text"                   placeholder="Item's Name"                   onChange={this.handleInputChange}                   name="name"                   className="input"                   />               </div>               <div>                 <input                   type="number"                   placeholder="Item's Price"                   onChange={this.handleInputChange}                   name="price"                   className="input"                   />               </div>               <button className="button is-grey">Submit</button>             </form>           </div>                      <div className="box">             {               this.state.list.length ? (                 this.state.list.map(item => (                   <ul>                     <li key={item.id}>                       <p>{item.name}</p>                       <p>$  {item.price}</p>                     </li>                     <hr />                   </ul>                 ))               ) : <p>Your list is empty</p>             }           </div>         </section>       </React.Fragment>     )   } }  ReactDOM.render(   <App />,   document.getElementById('root') );

As items are added to the list, we need to update the state of the list to reflect those new items. To update the state of list using setState(), we’ll have to do this:

handleSubmit = (e) => {   e.preventDefault()   const newItem = {     id: uuid.v4(),     name: this.state.name,     price: this.state.price   }   this.setState({ list: [...this.state.list, newItem] }) };

If you have to update multiple states in the application, you’ll have to do a ton of spreading to create a new state using the old state and the additional value. Which can look more complex as the number of changes increases. With Immer, it becomes very easy to do that, as we did in the example above.

What if we want to add a function that gets called as a callback after the state update?In this case, let’s say we are keeping a tally of the number of items in the list and the total price of all the items.

See the Pen
immer 3 – shopping list
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

Say we want to calculate the amount that will be spent based on the price of items in the list, we can have the handleSubmit function look like this:

handleSubmit = (e) => {   e.preventDefault()   const newItem = {     id: uuid.v4(),     name: this.state.name,     price: this.state.price   }      this.setState(     produce(draft => {       draft.list = draft.list.concat(newItem)     }), () => {       this.calculateAmount(this.state.list)     }   ) };

First, we create an object using the data entered by the user, which we then assign to newItem. To update our application’s state, we make use of .concat() which will return a new array that’s comprised of the previous items and the new item. This updated copy is now set as the value of draft.list, which can then be used by Immer to update the state of the application.

The callback function gets called after the state update. It’s important to note that it makes use of the updated state.

The function we want to call will look like this:

calculateAmount = (list) => {   let total = 0;     for (let i = 0; i < list.length; i++) {       total += parseInt(list[i].price, 10)     }   this.setState(     produce(draft => {       draft.totalAmount = total     })   ) }

Let’s look at Immer hooks

use-immer is a hook that allows you to manage state in your React application. Let’s see this in action using a classic counter example.

import React from "react"; import {useImmer} from "use-immer";  const Counter = () => {   const [count, updateCounter] = useImmer({     value: 0   });    function increment() {     updateCounter(draft => {       draft.value = draft.value +1;     });   }    return (     <div>       <h1>         Counter {count.value}       </h1>       <br />       <button onClick={increment}>Increment</button>     </div>   ); }  export default Counter;

useImmer is similar to useState. The function returns the state and an updater function. When the component loads at first, the value of the state (which is count in this example), is the same as the value passed to useImmer. Using the updater function which is returned, we can then create an increment function to increase the value of the count.

There is also a useReducer-like hook for Immer.

import React, { useRef } from "react"; import {useImmerReducer } from "use-immer"; import uuidv4 from "uuid/v4" const initialState = []; const reducer = (draft, action) => {   switch (action.type) {     case "ADD_ITEM":       draft.push(action.item);       return;     case "CLEAR_LIST":       return initialState;     default:       return draft;   } } const Todo = () => {   const inputEl = useRef(null);   const [state, dispatch] = useImmerReducer(reducer, initialState);      const handleSubmit = (e) => {     e.preventDefault()     const newItem = {       id: uuidv4(),       text: inputEl.current.value     };     dispatch({ type: "ADD_ITEM", item: newItem });     inputEl.current.value = "";     inputEl.current.focus();   }      const handleClear = () => {     dispatch({ type: 'CLEAR_LIST' })   }      return (     <div className='App'>       <header className='App-header'>         <ul>           {state.map(todo => {             return <li key={todo.id}>{todo.text}</li>;           })}         </ul>         <form onSubmit={handleSubmit}>           <input type='text' ref={inputEl} />           <button             type='submit'           >             Add Todo           </button>         </form>         <button           onClick={handleClear}         >           Clear Todos         </button>       </header>     </div>   ); } export default Todo;

useImmerReducer takes in a reducer function and the initial state, and it returns both state and the dispatch function. We can then loop through the state to display the items we have. We dispatch an action when submitting a todo item and clearing the list of them. The dispatched action has a type which we use in determining what to do in the reducer function.
In the reducer function, we make use of draft like we did before, instead of state. With that, we have a convenient way of manipulating the state of our application.

You can find the code used in the above example on GitHub.

That’s a look at Immer!

Going forward, you can begin to make use of Immer in your next project, or even slowly begin to use it in the current project you’re working on. It has proven to aid in making state management convenient.

The post Using Immer for React State Management appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Fetching Data in React using React Async

You’re probably used to fetching data in React using axios or fetch. The usual method of handling data fetching is to:

  • Make the API call.
  • Update state using the response if all goes as planned.
  • Or, in cases where errors are encountered, an error message is displayed to the user.

There will always be delays when handling requests over the network. That’s just part of the deal when it comes to making a request and waiting for a response. That’s why we often make use of a loading spinner to show the user that the expected response is loading.

See the Pen
ojRMaN
by Geoff Graham (@geoffgraham)
on CodePen.

All these can be done using a library called React Async.

React Async is a promised-based library that makes it possible for you to fetch data in your React application. Let’s look at various examples using components, hooks and helpers to see how we can implement loading states when making requests.

For this tutorial, we will be making use of Create React App. You can create a project by running:

npx create-react-app react-async-demo

When that is done, run the command to install React Async in your project, using yarn or npm:

## yarn yarn add react-async  ## npm npm install react-async --save

Example 1: Loaders in components

The library allows us to make use of <Async> directly in our JSX. As such, the component example will look like this;

// Let's import React, our styles and React Async import React from 'react'; import './App.css'; import Async from 'react-async';  // We'll request user data from this API const loadUsers = () =>   fetch("https://jsonplaceholder.typicode.com/users")     .then(res => (res.ok ? res : Promise.reject(res)))     .then(res => res.json())  // Our component function App() {   return (     <div className="container">       <Async promiseFn={loadUsers}>         {({ data, err, isLoading }) => {           if (isLoading) return "Loading..."           if (err) return `Something went wrong: $  {err.message}`            if (data)             return (               <div>                 <div>                   <h2>React Async - Random Users</h2>                 </div>                 {data.map(user=> (                   <div key={user.username} className="row">                     <div className="col-md-12">                       <p>{user.name}</p>                       <p>{user.email}</p>                     </div>                   </div>                 ))}               </div>             )         }}       </Async>     </div>   ); }  export default App;

First, we created a function called loadUsers. This will make the API call using the fetch API. And, when it does, it returns a promise which gets resolved. After that, the needed props are made available to the component.

The props are:

  • isLoading: This handles cases where the response has not be received from the server yet.
  • err: For cases when an error is encountered. You can also rename this to error.
  • data: This is the expected data obtained from the server.

As you can see from the example, we return something to be displayed to the user dependent on the prop.

Example 2: Loaders in hooks

If you are a fan of hooks (as you should), there is a hook option available when working with React Async. Here’s how that looks:

// Let's import React, our styles and React Async import React from 'react'; import './App.css'; import { useAsync } from 'react-async';  // Then we'll fetch user data from this API const loadUsers = async () =>   await fetch("https://jsonplaceholder.typicode.com/users")     .then(res => (res.ok ? res : Promise.reject(res)))     .then(res => res.json())  // Our component function App() {   const { data, error, isLoading } = useAsync({ promiseFn: loadUsers })   if (isLoading) return "Loading..."   if (error) return `Something went wrong: $  {error.message}`   if (data)      // The rendered component   return (     <div className="container">       <div>         <h2>React Async - Random Users</h2>       </div>       {data.map(user=> (         <div key={user.username} className="row">           <div className="col-md-12">             <p>{user.name}</p>             <p>{user.email}</p>           </div>         </div>       ))}     </div>   ); }  export default App;

This looks similar to the component example, but in this scenario, we’re making use of useAsync and not the Async component. The response returns a promise which gets resolved, and we also have access to similar props like we did in the last example, with which we can then return to the rendered UI.

Example 3: Loaders in helpers

Helper components come in handy in making our code clear and readable. These helpers can be used when working with an useAsync hook or with an Async component, both of which we just looked at. Here is an example of using the helpers with the Async component.

// Let's import React, our styles and React Async import React from 'react'; import './App.css'; import Async from 'react-async';  // This is the API we'll use to request user data const loadUsers = () =>   fetch("https://jsonplaceholder.typicode.com/users")     .then(res => (res.ok ? res : Promise.reject(res)))     .then(res => res.json())  // Our App component function App() {   return (     <div className="container">       <Async promiseFn={loadUsers}>           <Async.Loading>Loading...</Async.Loading>           <Async.Fulfilled>             {data => {               return (                 <div>                   <div>                     <h2>React Async - Random Users</h2>                   </div>                   {data.map(user=> (                     <div key={user.username} className="row">                       <div className="col-md-12">                         <p>{user.name}</p>                         <p>{user.email}</p>                       </div>                     </div>                   ))}                 </div>               )             }}           </Async.Fulfilled>           <Async.Rejected>             {error => `Something went wrong: $  {error.message}`}           </Async.Rejected>       </Async>     </div>   ); }  export default App;

This looks similar to when we were making use of props. With that done, you could break the different section of the app into tiny components.

Conclusion

If you have been growing weary of going the route I mentioned in the opening section of this tutorial, you can start making of React Async in that project you are working on. The source code used in this tutorial can be found in their different branches on GitHub.

The post Fetching Data in React using React Async appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Build a Chat App Using React Hooks in 100 Lines of Code

We’ve looked at React Hooks before, around here at CSS-Tricks. I have an article that introduces them as well that illustrates how to use them to create components through functions. Both articles are good high-level overviews about the way they work, but they open up a lot of possibilities, too.

So, that’s what we’re going to do in this article. We’re going to see how hooks make our development process easier and faster by building a chat application.

Specifically, we are building a chat application using Create React App. While doing so, we will be using a selection of React Hooks to simplify the development process and to remove a lot of boilerplate code that’s unnecessary for the work.

There are several open source Reacts hooks available and we’ll be putting those to use as well. These hooks can be directly consumed to build features that otherwise would have taken more of code to create. They also generally follow well-recognized standards for any functionality. In effect, this increases the efficiency of writing code and provides secure functionalities.

Let’s look at the requirements

The chat application we are going to build will have the following features:

  • Get a list of past messages sent from the server
  • Connect to a room for group chatting
  • Get updates when people disconnect from or connect to a room
  • Send and receive messages

We’re working with a few assumptions as we dive in:

  • We’ll consider the server we are going to use as a blackbox. Don’t worry about it working perfectly as we’re going to communicate with it using simple sockets.
  • All the styles are contained in a single CSS file, can be copied to the src directory. All the styles used within the app are linked in the repository.

Getting set up for work

OK, we’re going to want to get our development environment ready to start writing code. First off, React requires both Node and npm. You can set them up here.

Let’s spin up a new project from the Terminal:

npx create-react-app socket-client cd socket-client npm start

Now we should be able to navigate to http://localhost:3000 in the browser and get the default welcome page for the project.

From here, we’re going to break the work down by the hooks we’re using. This should help us understand the hooks as we put them into practical use.

Using the setState hook

The first hook we’re going to use is useState. It allows us to maintain state within our component as opposed to, say, having to write and initialize a class using this.state. Data that remains constant, such as username, is stored in useState variables. This ensures the data remains easily available while requiring a lot less code to write.

The main advantage of useState is that it’s automatically reflected in the rendered component whenever we update the state of the app. If we were to use regular variables, they wouldn’t be considered as the state of the component and would have to be passed as props to re-render the component. So, again, we’re cutting out a lot of work and streamlining things in the process.

The hook is built right into React, so we can import it with a single line:

import React, { useState } from 'react';

We are going to create a simple component that returns “Hello” if the user is already logged in or a login form if the user is logged out. We check the id variable for that.

Our form submissions will be handled by a function we’re creating called handleSubmit. It will check if the Name form field is completed. If it is, we will set the id and room values for that user. Otherwise, we’ll throw in a message reminding the user that the Name field is required in order to proceed.

// App.js  import React, { useState } from 'react'; import './index.css';  export default () => {   const [room, setRoom] = useState('');   const [id, setId] = useState('');    const handleSubmit = e => {     e.preventDefault();     const name = document.querySelector('#name').value.trim();     const room_value = document.querySelector('#room').value.trim();     if (!name) {       return alert("Name can't be empty");     }     setId(name);     setRoom(document.querySelector('#room').value.trim());   };    return id !== '' ? (     <div>Hello</div>   ) : (     <div style={{ textAlign: 'center', margin: '30vh auto', width: '70%' }}>       <form onSubmit={event => handleSubmit(event)}>         <input id="name" required placeholder="What is your name .." /><br />         <input id="room" placeholder="What is your room .." /><br />         <button type="submit">Submit</button>       </form>     </div>   ); };

That’s how we’re using the useState hook in our chat application. Again, we’re importing the hook from React, constructing values for the user’s ID and chat room location, setting those values if the user’s state is logged in, and returning a login form if the user is logged out.

Using the useSocket hook

We’re going to use an open source hook called useSocket to maintain a connection to our server. Unlike useState, this hook is not baked into React, so we’re going to have to add it to our project before importing it into the app.

npm add use-socket.io-client

The server connection is maintained by using the React Hooks version of the socket.io library, which is an easier way of maintaining websocket connections with a server. We are using it for sending and receiving real-time messages as well as maintaining events, like connecting to a room.

The default socket.io client library has global declarations, i.e., the socket variable we define can be used by any component. However, our data can be manipulated from anywhere and we won’t know where those changes are happening. Socket hooks counter this by constraining hook definitions at the component level, meaning each component is responsible for its own data transfer.

The basic usage for useSocket looks like this:

const [socket] = useSocket('socket-url')

We’re going to be using a few socket APIs as we move ahead. For the sake of reference, all of them are outlined in the socket.io documentation. But for now, let’s import the hook since we’ve already installed it.

import useSocket from 'use-socket.io-client';

Next, we’ve got to initialize the hook by connecting to our server. Then we’ll log the socket in the console to check if it is properly connected.

const [id, setId] = useState(''); const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');  socket.connect(); console.log(socket);

Open the browser console and the URL in that snippet should be logged.

Using the useImmer hook

Our chat app will make use of the useImmer hook to manage state of arrays and objects without mutating the original state. It combines useState and Immer to give immutable state management. This will be handy for managing lists of people who are online and messages that need to be displayed.

Using Immer with useState allows us to change an array or object by creating a new state from the current state while preventing mutations directly on the current state. This offers us more safety as far as leaving the current state intact while being able to manipulate state based on different conditions.

Again, we’re working with a hook that’s not built into React, so let’s import it into the project:

npm add use-immer

The basic usage is pretty straightforward. The first value in the constructor is the current state and the second value is the function that updates that state. The useImmer hook then takes the starting values for the current state.

const [data, setData] = useImmer(default_value)

Using the setData hook

Notice the setData hook in that last example? We’re using that to make a draft copy of the current data we can use to manipulate the data safely and use it as the next state when changes become immutable. Thus, our original data is preserved until we’re done running our functions and we’re absolutely clear to update the current data.

setData(draftState => {    draftState.operation();  });  // ...or  setData(draft => newState);  // Here, draftState is a copy of the current data

Using the useEffect hook

Alright, we’re back to a hook that’s built right into React. We’re going to use the useEffect hook to run a piece of code only when the application loads. This ensures that our code only runs once rather than every time the component re-renders with new data, which is good for performance.

All we need to do to start using the hook is to import it — no installation needed!

import React, { useState, useEffect } from 'react';

We will need a component that renders a message or an update based on the presence or absence of a sende ID in the array. Being the creative people we are, let’s call that component Messages.

const Messages = props => props.data.map(m => m[0] !== '' ?  (<li key={m[0]}><strong>{m[0]}</strong> : <div className="innermsg">{m[1]}</div></li>)  : (<li key={m[1]} className="update">{m[1]}</li>) );

Let’s put our socket logic inside useEffect so that we don’t duplicate the same set of messages repeatedly when a component re-renders. We will define our message hook in the component, connect to the socket, then set up listeners for new messages and updates in the useEffect hook itself. We will also set up update functions inside the listeners.

const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');       socket.connect();  const [messages, setMessages] = useImmer([]); useEffect(()=>{   socket.on('update', message => setMessages(draft => {     draft.push(['', message]);   }));    socket.on('message que',(nick, message) => {     setMessages(draft => {       draft.push([nick, message])     })   }); },0);

Another touch we’ll throw in for good measure is a “join” message if the username and room name are correct. This triggers the rest of the event listeners and we can receive past messages sent in that room along with any updates required.

// ...   setRoom(document.querySelector('#room').value.trim());   socket.emit('join', name, room); };  return id ? (   <section style={{display:'flex',flexDirection:'row'}} >     <ul id="messages"><Messages data={messages}></Messages></ul>     <ul id="online"> &#x1f310; :</ul>     <div id="sendform">       <form id="messageform" style={{display: 'flex'}}>         <input id="m" /><button type="submit">Send Message</button>       </form>     </div>   </section> ) : ( // ...

The finishing touches

We only have a few more tweaks to wrap up our chat app. Specifically, we still need:

  • A component to display people who are online
  • A useImmer hook for it with a socket listener
  • A message submission handler with appropriate sockets

All of this builds off of what we’ve already covered so far. I’m going to drop in the full code for the App.js file to show how everything fits together.

// App.js  import React, { useState, useEffect } from 'react';  import useSocket from 'use-socket.io-client';  import { useImmer } from 'use-immer';  import './index.css';  const Messages = props => props.data.map(m => m[0] !== '' ? (<li><strong>{m[0]}</strong> : <div className="innermsg">{m[1]}</div></li>) : (<li className="update">{m[1]}</li>) );  const Online = props => props.data.map(m => <li id={m[0]}>{m[1]}</li>);  export default () => {    const [room, setRoom] = useState('');    const [id, setId] = useState('');      const [socket] = useSocket('<https://open-chat-naostsaecf.now.sh>');   socket.connect();    const [messages, setMessages] = useImmer([]);      const [online, setOnline] = useImmer([]);      useEffect(()=>{     socket.on('message que',(nick,message) => {       setMessages(draft => {         draft.push([nick,message])       })     });        socket.on('update',message => setMessages(draft => {       draft.push(['',message]);     }))        socket.on('people-list',people => {       let newState = [];       for(let person in people){         newState.push([people[person].id,people[person].nick]);       }       setOnline(draft=>{draft.push(...newState)});       console.log(online)     });        socket.on('add-person',(nick,id)=>{       setOnline(draft => {         draft.push([id,nick])       })     })        socket.on('remove-person',id=>{       setOnline(draft => draft.filter(m => m[0] !== id))     })        socket.on('chat message',(nick,message)=>{       setMessages(draft => {draft.push([nick,message])})     })   },0);      const handleSubmit = e => {     e.preventDefault();     const name = document.querySelector('#name').value.trim();       const room_value = document.querySelector('#room').value.trim();     if (!name) {       return alert("Name can't be empty");     }     setId(name);     setRoom(document.querySelector('#room').value.trim());     console.log(room)     socket.emit("join", name,room_value);   };      const handleSend = e => {     e.preventDefault();     const input = document.querySelector('#m');     if(input.value.trim() !== ''){       socket.emit('chat message',input.value,room);       input.value = '';     }   }      return id ? (     <section style={{display:'flex',flexDirection:'row'}} >       <ul id="messages"><Messages data={messages} /></ul>       <ul id="online"> &#x1f310; : <Online data={online} /> </ul>       <div id="sendform">         <form onSubmit={e => handleSend(e)} style={{display: 'flex'}}>             <input id="m" /><button style={{width:'75px'}} type="submit">Send</button>         </form>       </div>     </section>   ) : (     <div style={{ textAlign: 'center', margin: '30vh auto', width: '70%' }}>       <form onSubmit={event => handleSubmit(event)}>         <input id="name" required placeholder="What is your name .." /><br />         <input id="room" placeholder="What is your room .." /><br />         <button type="submit">Submit</button>       </form>     </div>   ); };

Wrapping up

That’s it! We built a fully functional group chat application together! How cool is that? The complete code for the project can be found here on GitHub.

What we’ve covered in this article is merely a glimpse of how React Hooks can boost your productivity and help you build powerful applications with powerful front-end tooling. I have built a more robust chat application in this comprehensive tutorial. Follow along if you want to level up further with React Hooks.

Now that you have hands-on experience with React Hooks, use your newly gained knowledge to get even more practice! Here are a few ideas of what you can build from here:

  • A blogging platform
  • Your own version of Instagram
  • A clone of Reddit

Have questions along the way? Leave a comment and let’s make awesome things together.

The post Build a Chat App Using React Hooks in 100 Lines of Code appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Frontend Masters: The New, Complete Intro to React Course… Now with Hooks!

, , , , , ,
[Top]

Getting to Know the useReducer React Hook

useReducer is one of a handful of React hooks that shipped in React 16.7.0. It accepts a reducer function with the application initial state, returns the current application state, then dispatches a function.

Here is an example of how it is used;

const [state, dispatch] = useReducer(reducer, initialState);

What’s the good for? Well, think about any situation where having the first loaded state of the application might be nice. Let’s say the starting point on an interactive map. Maybe it’s an app that lets the user build a custom car with custom options from a default model. Here’s a pretty neat demo of a calculator app that puts useRedcuer to use in order to reset the calculator to a default state of zero when clearing it out.

See the Pen
Basic React Hook Calculator
by Gianpierangelo De Palma (@dpgian)
on CodePen.

We’re going to dig into a couple more examples in this post, but let’s first look at the hook itself to get a better idea of what it is and what exactly it does when it’s used.

The almighty reducer

It’s tough to talk about useState without also mentioning JavaScript’s reduce method. We linked it up at the very top, but Sarah’s post is an excellent overview of reducers and helps set the state for where we’re going here.

The first and most important thing to understand about a reducer is that it will always only return one value. The job of a reducer is to reduce. That one value can be a number, a string, an array or an object, but it will always only be one. Reducers are really great for a lot of things, but they’re especially useful for applying a bit of logic to a group of values and ending up with another single result.

So, if we have an array of numbers, reduce will distill it down to a single number that adds up for as many times as there are values. Say we have this simple array:

const numbers = [1, 2, 3]

…and we have a function that logs each time our reducer makes a calculation into the console. This will help us see how reduce distills the array into a single number.

const reducer = function (tally, number) {  	console.log(`Tally: $  {tally}, Next number: $  {number}, New Total: $  {tally + number}`) 	return tally + number }

Now let’s run a reducer on it. As we saw earlier, reduce takes dispatches a function that runs against a default state. Let’s plug our reducer function and an initial value of zero in there.

const total = numbers.reduce(reducer, 0)

Here’s what gets logged to the console:

"Tally: 0, Next number: 1, New Total: 1" "Tally: 1, Next number: 2, New Total: 3" "Tally: 3, Next number: 3, New Total: 6"

See how reduce takes an initial value and builds on it as each number in the array is added to it until we get a final value? In this case, that final value is 6.

I also really like this (modified) example from Dave Ceddia that shows how reduce can be used on an array of letters to spell a word:

var letters = ['r', 'e', 'd', 'u', 'c', 'e'];  // `reduce` takes 2 arguments: //   - a function to do the reducing (you might say, a "reducer") //   - an initial value for accumulatedResult var word = letters.reduce( 	function(accumulatedResult, arrayItem) { 		return accumulatedResult + arrayItem; 	}, ''); // <-- notice this empty string argument: it's the initial value  console.log(word) // => "reduce"

useReducer works with states and actions

OK, that was a lot of refresher to get what we’re really talking about: userReducer. It’s important to get all this, though, because you may have noticed where we’re going now after having seen the way reduce fires a function against an initial value. It’s the same sort of concept, but returns two elements as an array, the current state and a dispatch function.

In other words:

const [state, dispatch] = useReducer(reducer, initialArg, init);

What’s up with that third init argument? It’s an optional value that will lazily create the initial state. That means we can calculate the initial state/value with an init function outside of the reducer instead of providing an explicit value. That’s handy if the initial value could be different, say based on a last saved state instead of a consistent value.

To get it working, we need to do a few things:

  • Define an initial state.
  • Provide a function that contains actions that update the state.
  • Trigger userReducer to dispatch an updated state that’s calculated relative to the initial state.

The classic example of this a counter application. In fact, that’s what React’s docs use to drive the concept home. Here’s that put into practice:

See the Pen
React useReducer 1
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

It’s a good example because it demonstrates how an initial state (a zero value) is used to calculate a new value each time an action is fired by clicking either the increase or decrease button. We could even throw in a “Reset” button in there to clear the total back to the initial state of zero.

Example: A Car Customizer

See the Pen
React useReducer – car example
by Geoff Graham (@geoffgraham)
on CodePen.

In this example, we are making the assumption that the user has selected a car to purchase. However, we want the app to allow the user to add extra options to the car. Each option has a price that adds to the base total.

First, we need to create the initial state which will consist of the car, an empty array to keep track of features, and an additional price that starts at $ 26,395 and a list of items in the store, so the user can pick what they want.

const initialState = {   additionalPrice: 0,   car: {     price: 26395,     name: "2019 Ford Mustang",     image: "https://cdn.motor1.com/images/mgl/0AN2V/s1/2019-ford-mustang-bullitt.jpg",     features: []   },   store: [     { id: 1, name: "V-6 engine", price: 1500 },     { id: 2, name: "Racing detail package", price: 1500 },     { id: 3, name: "Premium sound system", price: 500 },     { id: 4, name: "Rear spoiler", price: 250 }   ] };

Our reducer function will handle two things: the addition and removal of new items.

const reducer = (state, action) => {   switch (action.type) {     case "REMOVE_ITEM":       return {         ...state,         additionalPrice: state.additionalPrice - action.item.price,         car: { ...state.car, features: state.car.features.filter((x) => x.id !== action.item.id)},         store: [...state.store, action.item]       };     case "BUY_ITEM":       return {         ...state,         additionalPrice: state.additionalPrice + action.item.price,         car: { ...state.car, features: [...state.car.features, action.item] },         store: state.store.filter((x) => x.id !== action.item.id)       }     default:       return state;   } }

When the user selects the item she wants, we update the features for the car, increase the additionalPrice and also remove the item from the store. We ensure that the other parts of the state remain as they are.
We do something similar when a user removes an item from the features list – reduce the additional price, return the item to the store.
Here is how the App component looks like.

const App = () => {   const inputRef = useRef();   const [state, dispatch] = useReducer(reducer, initialState);      const removeFeature = (item) => {     dispatch({ type: 'REMOVE_ITEM', item });   }      const buyItem = (item) => {     dispatch({ type: 'BUY_ITEM', item })   }      return (     <div>       <div className="box">         <figure className="image is-128x128">           <img src={state.car.image} />         </figure>         <h2>{state.car.name}</h2>         <p>Amount: $  {state.car.price}</p>         <div className="content">           <h6>Extra items you bought:</h6>           {state.car.features.length ?              (               <ol type="1">                 {state.car.features.map((item) => (                   <li key={item.id}>                     <button                       onClick={() => removeFeature(item)}                       className="button">X                     </button>                     {item.name}                   </li>                 ))}               </ol>             ) : <p>You can purchase items from the store.</p>           }         </div>       </div>       <div className="box">         <div className="content">           <h4>Store:</h4>           {state.store.length ?              (             <ol type="1">               {state.store.map((item) => (                 <li key={item.id}>\                   <button                     onClick={() => buyItem(item)}                     className="button">Buy                   </button>                   {item.name}                 </li>               ))}             </ol>             ) : <p>No features</p>           }         </div>          <div className="content">         <h4>           Total Amount: $  {state.car.price + state.additionalPrice}         </h4>       </div>       </div>     </div>   ); }

The actions that get dispatched contains the details of the selected item. We make use of the action type to determine how the reducer function will handle the updating of the state. You can see that the rendered view changes based on what you do – buying an item from the store removes the item from the store and adds it to the list of features. Also, the total amount gets updated. No doubt, there are some improvements that can be done to the application, this is only for learning purpose.

What about useState? Can’t we use that instead?

An astute reader may have been asking this all along. I mean, setState is generally the same thing, right? Return a stateful value and a function to re-render a component with that new value.

const [state, setState] = useState(initialState);

We could have even used the useState() hook in the counter example provided by the React docs. However, useReducer is preferred in cases where state has to go through complicated transitions. Kent C. Dodds wrote up a explanation of the differences between the two and (while he often reaches for setState) he provides a good use case for using userReducer instead:

If your one element of your state relies on the value of another element of your state, then it’s almost always best to use useReducer

For example, imagine you have a tic-tac-toe game you’re writing. You have one element of state called squares which is just an array of all the squares and their value[.]

My rule of thumb is to reach for useReducer to handle complex states, particularly where the initial state is based on the state of other elements.

Oh wait, we already have Redux for this!

Those of you who have worked with Redux already know everything we’ve covered here and that’s because it was designed to use the Context API to pass stored states between components — without having to pass props through other components to get there.

So, does useReducer replace Redux? Nope. I mean, you can basically make your own Redux by using it with the useContext hook, but that’s doesn’t mean Redux is useless; Redux still has plenty of other features and benefits worth considering.

Where have you used userReducer? Have you found clear-cut cases where it’s better than setState? Maybe you can experiment with the things we covered here to build something. Here are a few ideas:

  • A calendar that focus at today’s date but allows a user to select other dates. Maybe even add a “Today” button that returns the user to today’s date.
  • You can try improving on the car example – have a list of cars that users can purchase. You might have to define this in the initial state, then the user can add extra features they want with a charge. These features can be predefined, or defined by the user.

The post Getting to Know the useReducer React Hook appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Managing State in React using Unstated-Next

In a previous post, we saw how to manage state using Unstated. As you might recall, Unstated uses React’s built-in setState to allow you create components that can consume state by subscribing to a provider — like the React’s Context API.

Well, we’re going to build off that last post by looking at Unstated Next, a library that author Jamie Kyle identifies as the “spiritual successor” to his Unstated project. Unstated Next provides both React Hooks and the Context API for managing state. Unstated was a minimal abstraction to the idea of React Hooks before they were a fully-fledged thing. But now that Hooks in React are so good, that abstraction is unnecessary and Unstated Next simply incorporates them while providing an API to share state and logic with the Hooks.

We’re going to specifically look at how to manage state in both single and multiple components using Unstated Next. It might be helpful to check out the previous post on Unstated prior to moving ahead, but it’s not totally necessary.

Example: A minimal form component

To start off, we’ll create a tiny React application for a form that merely contains a text input for a person’s name and a button to submit it. When the button is clicked, we’ll display the name as a paragraph element above the form. The source code for this example is available on GitHub.

This is going to be a Bootstrap React application which we can spin up by using Create React App. Let’s install that and then change directories into the project folder.

npx create-react-app unstated-next-form cd unstated-next-form>

We need to add Unstated Next as a dependency:

## yarn yarn add unstated-next  ## npm npm install --save unstated-next

We’ll be making use of React Hooks and createContainer from Unstated Next, so let’s import those into the App component:

// src/App.js import React, { useState } from 'react'; import { createContainer } from "unstated-next";

Next, we will create a custom hook. We’ll have our state in it, which we can create using useState:

// src/App.js // ...same as before  const useForm = () => {   const [input, setValue] = useState("");   const [name, setName] = useState("Barney Stinson");    const handleInput = event => {     setValue(event.target.value);   };    const updateName = event => {     event.preventDefault();     setName(input);     setValue("");   };    return {     input,     name,     handleInput,     updateName,   }; };

We have two states defined here. input will be used for keeping track of values entered into the text input and it will be updated using the handleInput method. name will be updated when the button is clicked, which will trigger the updateName method.

OK, now we can create a container by passing our custom hook as a parameter to the createContainer() method.

// src/App.js // ...same as before  const FormContainer = createContainer(useForm);

This will create a container which we can use across our application. Yeah, you read that right, but let’s take one step at a time. We’re starting with this one component to see how it works with Unstated Next.

Now, let’s create a Form component that looks like this.

// src/App.js // ...same as before  const Form = () => {   const form = FormContainer.useContainer();   return (     <div>       <p>Hello! {form.name}</p>       <div>         <input           type="text"           value={form.input}           onChange={form.handleInput}         />         <button onClick={form.updateName}>Save</button>       </div>     </div>   ); };

We’re assigning the variable form to the value obtained from calling FormContainer.useContainer(). The value contains the states and methods defined in the custom hook we created above. With that, we can make use of the state and methods provided — but for that to happen, we have to wrap the Form component in a provider.

const App = () => (   <Form.Provider>     <Form />   </Form.Provider> )

With what you have learned so far, try building a minimal to-do application using Unstated Next. If you get stuck, feel free to check this repository to see how I made mine.

Example: Sharing state across multiple components

OK, so you got a hint earlier that we can use our form container anywhere we’d like. One of the benefits of using Unstated Next is that it makes it possible to share state across multiple components. To see how this works, we’ll build a tiny app that uses the form features we made above and also makes it possible to create to-do tasks using the same state. The name of the user can be updated in the form component, and this update will also be reflected in the to-do component. Two birds of a feather!

There’s a repo for this example as well, so feel free to clone or download it as we plow ahead.

Let’s spin up a new project and install the necessary dependencies:

npx create-react-app unstated-next-app cd unstated-next-app yarn unstated-next shortid

The state for the application will live in a separate file. We want to have the states for the form and to-do components in the store, and the methods needed for updating them too. Create a store.js file inside the src directory and make it look like this;

// src/store.js import { useState } from "react"; import shortid from "shortid" import { createContainer } from 'unstated-next' export const useStore = () => {   // Construct a list that contains two default tasks   const list = [     { id: 1, title: 'Write code' },     { id: 2, title: 'Buy milk' }   ]   const [input, setValue] = useState("");   // Let's set a legen -- wait for it -- dary default name that updates on form submit   const [name, setName] = useState("Barney Stinson");   const [todos, addTodo] = useState(list);   const [item, setTodo] = useState("");   const handleInput = event => {     setValue(event.target.value);   };   const updateName = event => {     event.preventDefault();     setName(input);     setValue("");   };   const handleTodo = event => {     setTodo(event.target.value);   };   const handleSubmit = event => {     event.preventDefault();     const value = {       id: shortid.generate(),       title: item     }     addTodo(todos.concat(value));     setTodo("");   };   return {     input,     name,     handleInput,     updateName,     todos,     item,     handleTodo,     handleSubmit   }; } export const StoreContainer = createContainer(useStore)

We make use of useState() to create the states we need. The methods are defined and all of this happens inside the custom hook, useStore(). We create the StoreContainer and then pass useStore() as a parameter to createContainer(). With that, we can make use of the StoreContainer in the necessary components where want to make use of the state and methods we have defined.

Starting with the form section, create a file called form.js and it should look like what I have below;

// src/form.js import React from "react"; import { StoreContainer} from "./store";  const FormComponent = () => {   const form = StoreContainer.useContainer();   return (     <div>       <p>Hello! {form.name}</p>       <div>         <input type="text" value={form.input} onChange={form.handleInput} />         <button onClick={form.updateName}>Change Name</button>       </div>     </div>   ); }; export default FormComponent;

We’re using StoreContainer to access the state and methods we need. We’ll do the same thing for the task component which you can create in a todo.js file.

// src/todo.js import React from "react"; import { StoreContainer } from "./store";  const TodoComponent = () => {   const todo = StoreContainer.useContainer();   return (     <div>       <p>Add Todos</p>       <input type="text" value={todo.item} onChange={todo.handleTodo} />       <button onClick={todo.handleSubmit}>Add</button>       <div>         <p>Dear {todo.name}, here are your current tasks;</p>         {todo.todos.map((item) => {           return (             <ul key={item.id}>               <li>{item.title}</li>             </ul>           );         })}       </div>     </div>   ); }; export default TodoComponent;

You can see that todo.name can only be updated in the FormComponent. That’s because we need a way to provide the state in both components. That’s why we’re going to turn again to Provider and add one in the App component just like we did in the previous example.

import React from 'react'; import TodoContainer from "./todo"; import FormContainer from "./form"; import { StoreContainer } from "./store"  function App() {   return (     <div className="App">       <StoreContainer.Provider>         <FormContainer />         <TodoContainer />       </StoreContainer.Provider>     </div>   ); } export default App;

There we go! By adding the provider, data can be taken from the form component, stored in the provider, and passed back to the task list. 💥

The post Managing State in React using Unstated-Next appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]