Tag: Hooks

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

, , , , , ,

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

, , , , , ,
[Top]

Haunted: Hooks for Web Components

I was just chatting with Dave and he told me about Haunted. It’s hooks, but for native web components! Pretty cool. I think the existence of stuff like this makes using web components more and more palatable — particularly in that totally-native no-build-step-needed-at-all kinda way.

I get that there are all sorts of issues with web components, but the things that typically turn me away from them are a lack of nice templating and rerendering and no state management.

But we can knock those two out right quick these days…

First, making a component like <my-app> is perfectly comfortable:

import { html } from "https://unpkg.com/lit-html/lit-html.js"; import { component } from "https://unpkg.com/haunted/haunted.js";  function App() {   return html`     <div class="module">       Hello, World!     </div>   `; }  customElements.define("my-app", component(App));

Then we could add some state with hooks:

import { html } from "https://unpkg.com/lit-html/lit-html.js"; import { component, useState} from "https://unpkg.com/haunted/haunted.js";  function App() {   const [name, setName] = useState("Chris");   return html`     <div class="module">       Hello, $  {name}!     </div>   `; }  customElements.define("my-app", component(App));

The CodePen Challenge this week is using the Star Wars API, so let’s make a fetch request and use that to fill state. That’s a great use case for useEffect.

import { html } from "https://unpkg.com/lit-html/lit-html.js"; import { component, useState, useEffect } from "https://unpkg.com/haunted/haunted.js";  function App() {      const [planets, setPlanets] = useState([]);   useEffect(() => {     fetch('https://swapi.co/api/planets/?page=2')       .then(response => {         return response.json();       })       .then(data => {         let planets = data.results;         // remove ones with no diameters         planets = planets.filter(planet => planet.diameter !== "0");         setPlanets(planets);       });   }, []);    return html`     <style>       /* Shadow DOM styles */     </style>     <div class="all-planets">       $  {planets.map(planet => html`         <div class="planet" style="--dia: $  {planet.diameter}px">            <span class="planet-name">               $  {planet.name}            </span>         </div>       `)}     </div>   `; }  customElements.define("my-app", component(App));

That’s a proper little web component!

See the Pen
Star Wars API with Haunted.js
by Chris Coyier (@chriscoyier)
on CodePen.

The post Haunted: Hooks for Web Components appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

What Hooks Mean for Vue

Not to be confused with Lifecycle Hooks, Hooks were introduced in React in v16.7.0-alpha, and a proof of concept was released for Vue a few days after. Even though it was proposed by React, it’s actually an important composition mechanism that has benefits across JavaScript framework ecosystems, so we’ll spend a little time today discussing what this means.

Mainly, Hooks offer a more explicit way to think of reusable patterns — one that avoids rewrites to the components themselves and allows disparate pieces of the stateful logic to seamlessly work together.

The initial problem

In terms of React, the problem was this: classes were the most common form of components when expressing the concept of state. Stateless functional components were also quite popular, but due to the fact that they could only really render, their use was limited to presentational tasks.

Classes in and of themselves present some issues. For example, as React became more ubiquitous, stumbling blocks for newcomers did as well. In order to understand React, one had to understand classes, too. Binding made code verbose and thus less legible, and an understanding of this in JavaScript was required. There are also some optimization stumbling blocks that classes present, discussed here.

In terms of the reuse of logic, it was common to use patterns like render props and higher-order components, but we’d find ourselves in similar “pyramid of doom” — style implementation hell where nesting became so heavily over-utilized that components could be difficult to maintain. This led me to ranting drunkenly at Dan Abramov, and nobody wants that.

Hooks address these concerns by allowing us to define a component’s stateful logic using only function calls. These function calls become more compose-able, reusable, and allows us to express composition in functions while still accessing and maintaining state. When hooks were announced in React, people were excited — you can see some of the benefits illustrated here, with regards to how they reduce code and repetition:

In terms of maintenance, simplicity is key, and Hooks provide a single, functional way of approaching shared logic with the potential for a smaller amount of code.

Why Hooks in Vue?

You may read through this and wonder what Hooks have to offer in Vue. It seems like a problem that doesn’t need solving. After all, Vue doesn’t predominantly use classes. Vue offers stateless functional components (should you need them), but why would we need to carry state in a functional component? We have mixins for composition where we can reuse the same logic for multiple components. Problem solved.

I thought the same thing, but after talking to Evan You, he pointed out a major use case I missed: mixins can’t consume and use state from one to another, but Hooks can. This means that if we need chain encapsulated logic, it’s now possible with Hooks.

Hooks achieve what mixins do, but avoid two main problems that come with mixins:

  • They allows us to pass state from one to the other.
  • They make it explicit where logic is coming from.

If we’re using more than one mixin, it’s not clear which property was provided by which mixin. With Hooks, the return value of the function documents the value being consumed.

So, how does that work in Vue? We mentioned before that, when working with Hooks, logic is expressed in function calls that become reusable. In Vue, this means that we can group a data call, a method call, or a computed call into another custom function, and make them freely compose-able. Data, methods, and computed now become available in functional components.

Example

Let’s go over a really simple hook so that we can understand the building blocks before we move on to an example of composition in Hooks.

useWat?

OK, here’s were we have, what you might call, a crossover event between React and Vue. The use prefix is a React convention, so if you look up Hooks in React, you’ll find things like useState, useEffect, etc. More info here.

In Evan’s live demo, you can see where he’s accessing useState and useEffect for a render function.

If you’re not familiar with render functions in Vue, it might be helpful to take a peek at that.

But when we’re working with Vue-style Hooks, we’ll have — you guessed it — things like: useData, useComputed, etc.

So, in order for us look at how we’d use Hooks in Vue, I created a sample app for us to explore.

In the src/hooks folder, I’ve created a hook that prevents scrolling on a useMounted hook and reenables it on useDestroyed. This helps me pause the page when we’re opening a dialog to view content, and allows scrolling again when we’re done viewing the dialog. This is good functionality to abstract because it would probably be useful several times throughout an application.

import { useDestroyed, useMounted } from "vue-hooks";  export function preventscroll() {   const preventDefault = (e) => {     e = e || window.event;     if (e.preventDefault)       e.preventDefault();     e.returnValue = false;   }    // keycodes for left, up, right, down   const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };    const preventDefaultForScrollKeys = (e) => {     if (keys[e.keyCode]) {       preventDefault(e);       return false;     }   }    useMounted(() => {     if (window.addEventListener) // older FF       window.addEventListener('DOMMouseScroll', preventDefault, false);     window.onwheel = preventDefault; // modern standard     window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE     window.touchmove = preventDefault; // mobile     window.touchstart = preventDefault; // mobile     document.onkeydown = preventDefaultForScrollKeys;   });    useDestroyed(() => {     if (window.removeEventListener)       window.removeEventListener('DOMMouseScroll', preventDefault, false);      //firefox     window.addEventListener('DOMMouseScroll', (e) => {       e.stopPropagation();     }, true);      window.onmousewheel = document.onmousewheel = null;     window.onwheel = null;     window.touchmove = null;     window.touchstart = null;     document.onkeydown = null;   }); } 

And then we can call it in a Vue component like this, in AppDetails.vue:

<script> import { preventscroll } from "./../hooks/preventscroll.js"; ...  export default {   ...   hooks() {     preventscroll();   } } </script>

We’re using it in that component, but now we can use the same functionality throughout the application!

Two Hooks, understanding each other

We mentioned before that one of the primary differences between hooks and mixins is that hooks can actually pass values from one to another. Let’s look at that with a simple, albeit slightly contrived, example.

Let’s say in our application we need to do calculations in one hook that will be reused elsewhere, and something else that needs to use that calculation. In our example, we have a hook that takes the window width and passes it into an animation to let it know to only fire when we’re on larger screens.

In the first hook:

import { useData, useMounted } from 'vue-hooks';  export function windowwidth() {   const data = useData({     width: 0   })    useMounted(() => {     data.width = window.innerWidth   })    // this is something we can consume with the other hook   return {     data   } }

Then, in the second we use this to create a conditional that fires the animation logic:

// the data comes from the other hook export function logolettering(data) {   useMounted(function () {     // this is the width that we stored in data from the previous hook     if (data.data.width > 1200) {       // we can use refs if they are called in the useMounted hook       const logoname = this.$  refs.logoname;       Splitting({ target: logoname, by: "chars" });        TweenMax.staggerFromTo(".char", 5,         {           opacity: 0,           transformOrigin: "50% 50% -30px",           cycle: {             color: ["red", "purple", "teal"],             rotationY(i) {               return i * 50             }           }         },         ...

Then, in the component itself, we’ll pass one into the other:

<script> import { logolettering } from "./../hooks/logolettering.js"; import { windowwidth } from "./../hooks/windowwidth.js";  export default {   hooks() {     logolettering(windowwidth());   } }; </script>

Now we can compose logic with Hooks throughout our application! Again, this is a contrived example for the purposes of demonstration, but you can see how useful this might be for large scale applications to keep things in smaller, reusable functions.

Future plans

Vue Hooks are already available to use today with Vue 2.x, but are still experimental. We’re planning on integrating Hooks into Vue 3, but will likely deviate from React’s API in our own implementation. We find React Hooks to be very inspiring and are thinking about how to introduce its benefits to Vue developers. We want to do it in a way that complements Vue’s idiomatic usage, so there’s still a lot of experimentation to do.

You can get started by checking out the repo here. Hooks will likely become a replacement for mixins, so although the feature still in its early stages, it’s probably a concept that would be beneficial to explore in the meantime.

(Sincere thanks to Evan You and Dan Abramov for proofing this article.)

The post What Hooks Mean for Vue appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]

Intro to React Hooks

Hooks make it possible to organize logic in components, making them tiny and reusable without writing a class. In a sense, they’re React’s way of leaning into functions because, before them, we’d have to write them in a component and, while components have proven to be powerful and functional in and of themselves, they have to render something on the front end. That’s all fine and dandy to some extent, but the result is a DOM that is littered with divs that make it gnarly to dig through through DevTools and debug.

Well, React Hooks change that. Instead of relying on the top-down flow of components or abstracting components in various ways, like higher-order components, we can call and manage flow inside of a component. Dan Abramov explains it well in his Making Sense of React post:

Hooks apply the React philosophy (explicit data flow and composition) inside a component, rather than just between the components. That’s why I feel that Hooks are a natural fit for the React component model.

Unlike patterns like render props or higher-order components, Hooks don’t introduce unnecessary nesting into your component tree. They also don’t suffer from the drawbacks of mixins.

The rest of Dan’s post provides a lot of useful context for why the React team is moving in this direction (they’re now available in React v16.7.0-alpha) and the various problems that hooks are designed to solve. The React docs have an introduction to hooks that, in turn, contains a section on what motivated the team to make them. We’re more concerned with how the heck to use them, so let’s move on to some examples!

The important thing to note as we get started is that there are nine hooks currently available, but we’re going to look at what the React docs call the three basic ones: useState(), useEffect, and setContext(). We’ll dig into each one in this post with a summary of the advanced hooks at the end.

Defining state with useState()

If you’ve worked with React at any level, then you’re probably familiar with how state is generally defined: write a class and use this.state to initialize a class:

class SomeComponent extends React.component {   constructor(props)   super(props);   this.state = {     name: Barney Stinson // Some property with the default state value       } }

React hooks allow us to scrap all that class stuff and put the useState() hook to use instead. Something like this:

import { useState } from 'react';      function SomeComponent() {   const [name, setName] = useState('Barney Stinson'); // Defines state variable (name) and call (setName) -- both of which can be named anything }

Say what?! That’s it! Notice that we’re working outside of a class. Hooks don’t work inside of a class because they’re used in place of them. We’re using the hook directly in the component:

import { useState } from 'react';      function SomeComponent() {   const [name, setName] = useState('Barney Stinson');      return     <div>       <p>Howdy, {name}</p>     </div> }

Oh, you want to update the state of name? Let’s add an input and submit button to the output and call setName to update the default name on submission.

import { useState } from 'react'      function SomeComponent() {   const [input, setValue] = useState("");   const [name, setName] = useState('Barney Stinson');      handleInput = (event) => {     setValue(event.target.value);   }      updateName = (event) => {     event.preventDefault();     setName(input);     setValue("");   }      return (     <div>       <p>Hello, {name}!</p>       <div>         <input type="text" value={input} onChange={handleInput} />         <button onClick={updateName}>Save</button>       </div>     </div>   ) }

See the Pen React Hook: setState Example by Geoff Graham (@geoffgraham) on CodePen.

Notice something else in this example? We’re constructing two different states (input and name). That’s because the useState() hook allows managing multiple states in the same component! In this case, input is the property and setValue holds the state of the input element, which is called by the handleInput function then triggers the updateName function that takes the input value and sets it as the new name state.

Create side effects with useEffect()

So, defining and setting states is all fine and dandy, but there’s another hook called useEffect() that can be used to—you guessed it—define and reuse effects directly in a component without the need for a class or the need to use both redundant code for each lifecycle of a method (i.e. componentDidMount, componentDidUpdate, and componentWillUnmount).

When we talk about effects, we’re referring to things like API calls, updates to the DOM, and event listeners, among other things. The React documentation cites examples like data fetching, setting up subscriptions, and changing the DOM as possible use cases for this hook. Perhaps the biggest differentiator from setState() is that useEffect() runs after render. Think of it like giving React an instruction to hold onto the function that passes and then make adjustments to the DOM after the render has happened plus any updates after that. Again, the React documentation spells it out nicely:

By default, it runs both after the first render and after every update. […] Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.

Right on, so how do we run these effects? Well, we start off by importing the hook the way we did for setState().

import { useEffect } from 'react';

In fact, we can call both setState() and useEffect() in the same import:

import { useState, useEffect } from 'react';

Or, construct them:

const { useState, useEffect } = React;

So, let’s deviate from our previous name example by hooking into an external API that contains user data using axios inside the useEffect() hook then renders that data into a list of of users.

First, let’s bring in our hooks and initialize the App.

const { useState, useEffect } = React  const App = () => {   // Hooks and render UI }

Now, let’s put setState() to define users as a variable that contains a state of setUsers that we’ll pass the user data to once it has been fetched so that it’s ready for render.

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);   // Our effects come next }

Here’s where useEffect() comes into play. We’re going to use it to connect to an API and fetch data from it, then map that data to variables we can call on render.

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);      useEffect(() => {     // Connect to the Random User API using axios     axios("https://randomuser.me/api/?results=10")       // Once we get a response, fetch name, username, email and image data       // and map them to defined variables we can use later.       .then(response =>         response.data.results.map(user => ({           name: `{user.name.first} $  {user.name.last}`,           username: `{user.login.username}`,           email: `{user.email}`,           image: `{user.picture.thumbnail}`         }))       )       // Finally, update the `setUsers` state with the fetched data       // so it stores it for use on render       .then(data => {         setUsers(data);       });   }, []);      // The UI to render }

OK, now let’s render our component!

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);    useEffect(() => {     axios("https://randomuser.me/api/?results=10")       .then(response =>         response.data.results.map(user => ({           name: `{user.name.first} $  {user.name.last}`,           username: `{user.login.username}`,           email: `{user.email}`,           image: `{user.picture.thumbnail}`         }))       )       .then(data => {         setUsers(data);       });   }, []);      return (     <div className="users">       {users.map(user => (         <div key={user.username} className="users__user">           <img src={user.image} className="users__avatar" />           <div className="users__meta">             <h1>{user.name}</h1>             <p>{user.email}</p>           </div>         </div>       ))}     </div>   ) }

Here’s what that gets us:

See the Pen React Hook: setEffect example by Geoff Graham (@geoffgraham) on CodePen.

It’s worth noting that useEffect() is capable of so, so, so much more, like chaining effects and triggering them on condition. Plus, there are cases where we need to cleanup after an effect has run—like subscribing to an external resource—to prevent memory leaks. Totally worth running through the detailed explanation of effects with cleanup in the React documentation.

Context and useContext()

Context in React makes it possible to pass props down from a parent component to a child component. This saves you from the hassle of prop drilling. However, you could only make use of context in class components, but now you can make use of context in functional components using useContext() . Let’s create a counter example, we will pass the state and functions which will be used to increase or decrease the count from the parent component to child component using useContext(). First, let’s create our context:

const CountContext = React.createContext();

We’ll declare the count state and increase/decrease methods of our counter in our App component and set up the wrapper that will hold the component. We’ll put the context hook to use in the actual counter component in just a bit.

const App = () => {   // Use `setState()` to define a count variable and its state   const [count, setCount] = useState(0);      // Construct a method that increases the current `setCount` variable state by 1 with each click   const increase = () => {     setCount(count + 1);   };      // Construct a method that decreases the current `setCount` variable state by 1 with each click.   const decrease = () => {     setCount(count - 1);   };    // Create a wrapper for the counter component that contains the provider that will supply the context value.   return (     <div>       <CountContext.Provider         // The value is takes the count value and updates when either the increase or decrease methods are triggered.         value={{ count, increase, decrease }}       >         // Call the Counter component we will create next         <Counter />       </CountContext.Provider>     </div>   ); };

Alright, onto the Counter component! useContext() accepts an object (we’re passing in the CountContext provider) and allows us to tell React exactly what value we want (`count) and what methods trigger updated values (increase and decrease). Then, of course, we’ll round things out by rendering the component, which is called by the App.

const Counter = () => {   const { count, increase, decrease } = useContext(CountContext);   return (     <div className="counter">       <button onClick={decrease}>-</button>       <span className="count">{count}</span>       <button onClick={increase}>+</button>     </div>   ); };

And voilà! Behold our mighty counter with the count powered by context objects and values.

See the Pen React hooks – useContext by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Wrapping up

We’ve merely scratched the surface of what React hooks are capable of doing, but hopefully this gives you a solid foundation. For example, there are even more advanced hooks that are available in addition to the basic ones we covered in this post. Here’s a list of those hooks with the descriptions offered by the documentation so you can level up now that you’re equipped with the basics:

Hook Description
userReducer() An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method.
useCallback() Returns a memoized callback. Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.
useMemo() Returns a memoized value. Pass a “create” function and an array of inputs. useMemo will only recompute the memoized value when one of the inputs has changed.
useRef() useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
useImperativeMethods useImperativeMethods customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeMethods should be used with forwardRef.
useLayoutEffect The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

The post Intro to React Hooks appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]