Latest Tutorials

Learn about the latest technologies from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

CSS Reset For Composable Layouts

The styles that we write are not the first styles that get applied to our app. Before a single line of our CSS is used, the browsers will apply their user-agent style sheets. Unfortunately, there is no rule that requires any of the browsers to use the same style sheet for their user-agent. For this reason, CSS Resets have been a valuable tool to help developers provide consistent styling on the web. A CSS Reset is nothing more than a style sheet you bring in before the rest of your app's styles, either as a separate style sheet or tagged onto the beginning of your style sheet. This style sheet's goal is simply to provide a base from which you can consistently apply CSS across browsers. Some resets are aggressive and remove all styles from all elements. Others try to "normalize" all the user-agent style sheets of the various browsers. Luckily, there is currently less inconsistency across the browsers than would justify aggressive resets. Still, from a layout perspective, there is a need to override the browser default styles to make compositional layout possible. It makes sense to look at what you need to reset in the browser's user-agent style sheets to achieve this goal. The first thing to set is the box-sizing property on all elements and pseudo-elements to border-box : Setting the box-sizing property to border-box on all elements and pseudo-elements allows for a more intuitive developer experience since the size of the element will be calculated from border to border instead of the default, which is content + padding + border. After that, we remove any margins from all the elements. Doing this allows elements the ability to better control the space between their child elements. It is difficult to layout items correctly if they already have a built-in margin that you have to override. From there, we remove the padding and list style from ul and ol elements. A common thing that most people do when working with lists is remove the default padding and list styles added by browsers. Since this is pretty much universally done every time one styles a list, I like to do it once and get it done with. You will notice that we are using the attribute selector to remove the padding and list style only if the class attribute is set. Doing this will allow our lists to be reset if we are actively styling the element using the class attribute. By doing it this way, our list elements retain their default styles when we use the pure ul and ol tags without styling applied. Next, we set the min-block-size of the body to be 100vh It is helpful to have the body take up the entire viewport even if its content does not. Next, we set our images to be block-level elements instead of inline-level elements and then set their max-inline-size to 100% . Setting the max-inline-size to 100% makes our images responsive by default and treats images as block-level elements, which is how most people use img tags. Finally, we set the max-inline-size of text-based tags to be 60ch . The ch unit is approximately the width of the 0 character of the font family in use. In any given font family, each character can either be very wide like the letter W or very skinny like the letter l. The 0 is neither the widest nor skinniest character in a font family and is a good proxy for a character width. We use 60ch because a large amount of research has gone into finding the optimal line lengths for readability. You can check out the work done over at material.io to read more about optimal line lengths, but for brevity, 60 characters is a good default cap on the inline size of the text. With these resets in place, we have now primed the browser to let us start building composable layouts.

Thumbnail Image of Tutorial  CSS Reset For Composable Layouts

Encapsulated CSS

Photo by  Mika Baumeister  on  Unsplash Most modern frameworks, like React, use components as their foundation. They do this for a few reasons, but a crucial one is that components allow you to break your app into simple single-purpose parts that can then be composed together to solve more complex needs. Unfortunately, CSS was invented to solve problems from the top down,  starting with more general rules to more specific rules . Components encourage you to start from the bottom up, breaking your pages down into the more specific parts first, often in isolation to the whole, and then composing them together.  Many tools and naming conventions have been created to help us maintain our style sheets like BEM, SASS, Less, CSS-Modules, and CSS-in-JS, but they all fall short in the one problem that tooling can never solve:  Which component should be in charge of which styles? The answer to this question is key to making composition work, especially where web layout is concerned. That answer is Encapsulated CSS. Encapsulated CSS is a term that summarizes the rules of how to apply CSS styles in a composable way. It’s based on the programming principle of encapsulation. If we were to define encapsulation in a language-agnostic way, encapsulation involves grouping related things and restricting access to those things except through appropriate channels. For example, many program languages utilize a module system, which follows the principles of encapsulation. When you import a module like React , you get a group of functions that help you build a React application, but you don’t have access to the actual internals that make a React applications work.  Encapsulated CSS is based on that same principle. It’s a methodology that helps group-related styles at the correct component level and only applies styles through appropriate channels. There are two essential principles of Encapsulated CSS: The first principle is:  “Elements do not lay themselves out.”  When I say that elements don’t lay themselves out, I am specifically speaking to an element's position, size, and margin. The second principle is:  “Elements style themselves and layout only their immediate children.”  Properties that involve the border-box and inward are considered part of the component and therefore they should be applied at the element level. This also includes the layout environment of the component’s immediate children. Therefore, it is the parent component’s responsibility to set its direct children's position, size, and margin. If a component shouldn’t set its own layout properties, how do you set them? I mentioned earlier that encapsulation allows you to access properties through appropriate channels. These appropriate channels in components are props and the direct child combinator. In React, we use props as inputs to our component, much like functions use arguments. Our components use these props in our components. Just like we can expose a label or an onClick prop, we can expose layout properties like margin-top or min-width: Using props in this manner works well with one or two properties, but it becomes unwieldy quickly as you expose more and more properties. The props of your component should also be a reflection of what your component does. Having an arbitrary marginLeft prop doesn’t make sense on a calendar component. The other channel for adding styles is using the direct child combinator in the parent component. This tool allows us to select any or all of our parent containers’ immediate children and apply the layout styling we need.  Let’s take the following Blog Post component: Applying styles that follow the principles of Encapsulated CSS, we could do something like this: In the above stylesheet, the .blog-post and .blog-title classes set their own non-layout-related styles. However, when we needed to set layout properties on any of the elements, we used the direct child combinator to select the appropriate children of the .blog-post to set those properties. For example, we use  .blog-post > h2 to select the h2 tags that were direct children of the .blog-post class to set the layout properties we needed. At this point, one might start to ask why bother to go through this ceremony of adding a separate rule for the direct children just to add a layout property? One could argue that you could write less CSS by just applying the layout properties to the corresponding element.  First of all, because we didn’t add any layout properties to the .blog-post class, this blog post component can safely be placed in any context and not interfere with the layout environment that it is being put. The children can also now be refactored into components, if needed, and won’t bring along the baggage of the layout environment it was initially created in. It also helps with debugging. One of the most significant difficulties of debugging CSS is determining where your styles are coming from. It is easier to track down styles if you have rules in place where those style properties originate. If the style you are looking for is layout-related, you know that the style must be on the parent element. Encapsulated CSS is about creating restrictions that make sense and encourages you to write code that is easier to maintain and refactor. By following these rules, your components will become much easier to refactor and compose anywhere in your app.

Thumbnail Image of Tutorial Encapsulated CSS

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Set up a Django App to Respond to S3 Events on AWS Lambda

I'm going to show you how to set up your Django app to respond to S3 events on AWS Lambda. The tool we're using to get Django running on AWS Lambda is Zappa . Follow along to see how responding to events works with this setup. ... When running your Django app on AWS Lambda using Zappa, it's easy to send and receive events from other AWS services . Zappa lets you get events from: Let's configure our Django with Zappa project to detect when new files are uploaded to an S3 bucket. You'll need a Django project configured with Zappa. You can create a Django project, then follow the guide for adding Zappa to your Django setup . Once your project is set up properly, log into the AWS console and create an S3 bucket with a unique name. The bucket won't need public access for this tutorial since we'll upload files directly using the AWS console. Once the bucket is ready, click on 'Properties' and record the Amazon Resource Name (ARN) for the bucket. At the root of your project, create a new file called aws_events.py . In this file, we'll add the handler function that will accept all our S3 events. When AWS invokes your function, two objects are passed in: the event object and the context object. The context object contains metadata about the invocation, function, and execution environment. The S3 event contains: Here's the code to accept the event: Our event handler code above prints out the event information and exits. Finally, we need to tell Zappa to register our event handler function with AWS so that AWS can start sending events. Add the following to the project's zappa_settings.json file: With all the changes saved, go ahead and push these changes to the cloud. In order to see the event details that our code prints out, let's activate the Zappa console log. You can do this by running: This gets Zappa to show you what your Django project is printing. Leave it up and running and remember you can exit anytime by pressing Ctrl-C . Head back over to your AWS console and find your S3 bucket page. Under the Objects tab, find the upload button. Go ahead and upload a file. Any file will do, but it's best to send a smaller one for expediency. After a few seconds, you should see in the terminal window the full event information from S3. It should look something like the following: A few things to note here. The filename is testfile.jpg and the bucket name is newline-upload-bucket . Of course, your file and bucket will be named differently. Also note the formatting is a little wonky, but still readable. You can try uploading a few more files. Maybe try two or three at once. You'll see each upload handled by your Django project as independent events. At this point, we have many options. Maybe your app will create a thumbnail if an image was uploaded. Or scan a document for keywords. Alternatively, your app might send an email to the client that uploaded the file confirming the receipt of the file. To dive in even more into setting up your Python apps to run serverless on AWS Lambda, check out our latest course, The newline Guide to Serverless Django with Zappa. 

Thumbnail Image of Tutorial Set up a Django App to Respond to S3 Events on AWS Lambda

Scaffolding a React Component Library with Storybook (Using TypeScript)

When a company develops and releases a new product onto its platform, users expect this product to deliver an experience similar to another product (on the platform) they have worked with. For example, many people, including yourself, are probably familiar with at least one of Google's collaborative office tools, such as Google Sheets and/or Google Docs , that integrate seamlessly with Google Drive . Now, suppose Google announces a new collaborative office tool at Google I/O. If you decide to use this tool, then you may notice how much faster it takes for you to learn this tool, along with its shortcuts and tricks, because the interface contains features that you have previously interacted with in other tools. Across each tool, the appearance of these features, such as the editing toolbar and sharing dialog, remains consistent since they draw upon the same set of foundational elements, controls, colors, typography, animations, etc. By building a component library, we can centralize all reusable components at one location and access these components from any of our products. Furthermore, pairing a component library with a design system unifies every product within the platform under a singular brand identity. For distributed teams that work on products independently of one another, this allows teams to follow the same design principles/philosophies/patterns, share code and create components in isolation. Writing components in an environment outside of our application makes them flexible and adaptable to any specific layout requirements. This way, the component's design can account for both known and unforeseen use cases. Anytime designers, developers and product managers contribute to the library and update the components, those changes immediately propagate down to the products using those components. With so many different stakeholders involved, we need to build durable components that are thoroughly tested and well-documented. A popular open-source tool for organizing and building components in isolation is Storybook , which comes with a sandbox for previewing/demoing components and mocking use cases to capture different states of a component in stories . Documenting these use cases as stories especially helps in onboarding new team members. Storybook has integrations with different front-end libraries/frameworks: React , Vue , Angular , Svelte , etc. Additionally, if you need functionality that Storybook doesn't already provide, then you can create addons to extend Storybook. Below, I'm going to show you how to add Storybook to your component library. To get started, clone the following repository: This repository contains a component library with customizable D3 visualizations written in React and TypeScript. Currently, this repository only has one visualization component, a scatterplot. Inside of the project directory, install the dependencies: For this tutorial, we will be adding Storybook to this library and writing stories for its scatterplot component. Add Storybook to the library with Storybook CLI. As of Storybook v6.0, Storybook CLI automatically detects whether the project is TypeScript-based and configures Storybook to support TypeScript without any additional configuration. This command creates the following directories and files within the project: Within package.json , several Storybook dependencies are now listed under devDependencies . Along with these new dependencies, several NPM scripts are added for running Storybook locally and building Storybook as a static web application (to host on a cloud service and publish online). Run the storybook NPM script to run Storybook locally. This command spins up Storybook on localhost:6006 and automatically opens Storybook inside the browser. Once Storybook loads, you will be presented an introductory page that contains links to additional learning resources. Note : You can modify this page by editing the src/stories/Introduction.stories.mdx file. Each *.stories.tsx file defines a component's stories. To view a component's stories in Storybook, click on the item in the left sidebar that corresponds to the component to expand a list of its stories. For example, if you click on the "Button" item, then the canvas displays the first story listed for the <Button /> component. Rendered as an iframe, the canvas allows components to be tested in isolation. Altogether, four stories appear beneath the "Button" item in the sidebar: Each story describes how certain parameters affect the rendering of the component. To understand what this means, let's look inside the <Button /> component's source and story files. ( src/stories/Button.tsx ) ( src/stories/Button.stories.tsx ) Template is a function that accepts args and uses them to render the component. It serves as a template for defining a story. To keep the example simple, args represents props that are passed directly to the component, but they can be modified inside of the Template function for more complex examples. Each story makes a new copy of this template via Template.bind({}) to set its own properties. To specify a story's args , define an args property on the story's copy of the template function, and assign this property an object with the values needed for rendering the component. For example, the story named Primary renders the <Button /> component with the props { primary: true, label: "Button" } (passed directly to the component within the story's Template function via args ). This adds the storybook-button--primary CSS class to the <button /> element and sets its text to "Button." If you want to experiment with different prop values, then adjust the props within the "Controls" panel below the canvas. Only props of primitive types, such as booleans and strings, are dynamically editable. When you enter "red" into the backgroundColor input field, the button's background color changes to red. If you switch from "Controls" to "Actions," then you can see logs of event handlers executed as a result of user interactions. For example, the <Button /> component receives an onClick prop that attaches to its <button /> element. When you click the button in Storybook, the panel will print information about that onClick event. Everything mentioned above also applies to the stories Secondary , Large and Small . If you press the "Docs" tab, then Storybook shows information about the <Button /> component, such as prop descriptions, the code required to render the component shown in a story, etc. The prop descriptions come from inline comments written in the props' exported TypeScript interface. First, let's remove the example stories created during the initialization process. Next, let's recreate the src/stories/Introduction.stories.mdx file with the contents of the library's README.md file. ( src/stories/Introduction.stories.mdx ) @storybook/addon-docs/blocks provides the building blocks for writing documentation pages. For now, the introductory page will have a webpage title of "Example/Introduction" and will render the extracted contents of the README file to both "Canvas" and "Docs." Now, let's write some stories for our library's <Scatterplot /> component. Create a src/stories/Scatterplot.stories.tsx file. Inside of this file, add stories to reflect the following basic use cases for the <Scatterplot /> component: For all of the stories to access data fetched from a remote source, we must set a global loader, which runs before the rendering of the stories, inside of the .storybook/preview.js file. ( .storybook/preview.js ) Here, scatterplotData contains the fetched and processed Iris data, which will be available to the stories of the <Scatterplot /> component. scatterplotData can be accessed by any story template in the project via the template's second argument, the story context, which has a loaded property for accessing loader data. Back to the src/stories/Scatterplot.stories.tsx file, import the Story and Meta types from @storybook/react and export an object with metadata about the component. The pages of the component's stories will be prefixed with the webpage title "Example/Scatterplot." ( src/stories/Scatterplot.stories.tsx ) Define an object ( BASE_ARGS ) with template arguments shared by all of the stories. In this case, each story's scatterplot will have the same dimensions ( dimensions ) and render with the same axes' labels and data ( labels , xAccessorKey and yAccessorKey ). ( src/stories/Scatterplot.stories.tsx ) Write a template function that renders the <Scatterplot /> component based on args set for each story. Since the default value of data is an empty array, we must explicitly check for a flag isEmpty , which will notify the template function to use this empty array only for the "Empty" story. For the other stories, use the scatterplot data fetched by the global loader. ( src/stories/Scatterplot.stories.tsx ) Note : loaded is undefined when accessing the component's docspage. Unfortunately, all of the inline-rendered stories will be empty because loaders are experimental and not yet compatible with inline-rendered stories in Storybook Docs . Write the stories. For the "Default" story, just use the base arguments. For the "Empty" story, make sure to notify the template function to use the default empty data array. For the "Legend" story, set two additional fields to args : one for categoryKey (a key to access a record's category) and another for categoryColors (a list of colors to visually differentiate categories). ( src/stories/Scatterplot.stories.tsx ) Altogether... ( src/stories/Scatterplot.stories.tsx ) Default Story Empty Story Legend Story <Scatterplot /> Component's DocsPage For a final version of this tutorial, check out the GitHub repository here . Try integrating Storybook into your own component library!

Thumbnail Image of Tutorial Scaffolding a React Component Library with Storybook (Using TypeScript)

Writing Custom React Hooks for D3 Visualizations

In late 2018, the React development team introduced hooks into version 16.8 of the React library. Shifting from class components to functional components, hooks provide a cleaner pattern for reusing stateful logic between components without relying on higher-order components (also known as "wrappers"), which add unnecessary levels to the component hierarchy, and render props , which are messy because of the amount of code they add to the JSX returned by the render method. In class components, lifecycle methods and component state are rigidly bound to a particular component. Since class components define each lifecycle method only once, a lifecycle method often contains lots of unrelated logic, such as the componentDidMount lifecycle method setting up different event listeners and fetching data from a remote API endpoint. Performing all of these tasks within a single lifecycle method increases the likelihood of introducing bugs and unintended behavior. However, in functional components, all of this logic can be distributed into separate hooks to share this logic with other functional components. Built-in hooks, such as useState and useEffect , handle component state and side-effects ( useEffect consolidates multiple lifecycle methods into a single hook) respectively and make it easy to extract reusable snippets of code into smaller units of functionality. They serve as the building blocks for composing custom hooks. As a React application grows and becomes more complex to accommodate new features, writing components as functional components and delegating shareable logic to custom hooks allow components to not only be lighter, but also, flexible and maintainable. Below, I'm going to show you how to write custom React hooks. When writing custom hooks, there are some best practices that improve the clarity of these hooks for other developers. By convention, the name of a custom hook follows camel casing and starts with the use prefix. Additionally, the name of the hook should describe its purpose. For example, a hook named useCsv implies a hook that fetches (and processes) CSV data and notifies the component when the data is available for the component's rendering. The signature of a custom hook does not adhere to any strict rules. It can accept any number of arguments of any type and return any number of values of any type. Custom hooks follow all of the same rules as built-in hooks: Whenever a hook is called within a component, it has isolated local state that is independent of other hook calls. If you call the twice in a single component or call it in two different components, then each call yields state values unaffected by other calls. In this tutorial, we will create multiple draggable, resizable widgets, which are common in analytics dashboard. Each widget displays a simple data visualization. To keep the example simple, there will only be two widgets: one displays a bar chart that shows the frequency of a categorical variable (flower species) and one displays a scatterplot that plots two quantitative variables (petal length against petal width). Both of these visualizations consume the Iris flower data set . Try it out in the CodeSandbox demo below: Often, functional components in React applications contain similar pieces of functionality such as subscribing to a window event. In this example, both widgets possess the following characteristics, which all require stateful logic: If we extract these pieces of functionality and refactor them into custom hooks, then we can keep our code DRY and leverage them in future visualization components. With custom hooks, we can cleanly express complex logic. For the characteristics listed above, let's create five custom hooks to encapsulate them: When the user resizes a widget by dragging its bottom-right corner, the dimensions of the widget's visualization scale proportionally based on the new widget's new dimensions. When the height of the widget exceeds its base height, then the visualization's height will increase proportionally while preserving its base aspect ratio. When the height of the widget shrinks below its base height, then the visualization will maintain its base height (and width), and the user would need to scroll within the widget to view the hidden portions of the visualization. The useDimensions hook requires only one argument: baseDimensions . baseDimensions contains the starting dimensions (height, width and margins) of the visualization and the height of the drag bar. By knowing the starting dimensions of the visualization, the hook can calculate its aspect ratio and preserve the visualization's aspect ratio anytime the widget is resized. To listen for the resize event, the useDimensions hook creates a new instance of ResizeObserver within useEffect to observe for changes on a target, which in this case is the widget. Inside of the ResizeObserver callback, the widget is referenced as entry . When resizing the widget, the current width and height of the widget are referenced as entry.contentRect.width and entry.contentRect.height respectively. However, when determining the current dimensions of the visualization, the drag bar height must be subtracted from the widget height to obtain the new visualization height. This height is multiplied by the base aspect ratio to obtain the new visualization width. This hook returns the new visualization dimensions and a ref to set to the resizable element. This element happens to be a <div /> within the <VizResizer /> component, which represents the widget itself. ( src/hooks/useDimensions.js ) When the user clicks on the dark-gray bar within a widget and drags on it, the widget drags with respect to the mouse cursor's position. When the user releases it, the widget remains in the spot it was released at. If multiple widgets happen to overlap one another, then the most recently dragged widget appears above the others. The useDragDrop hook requires one argument: ref . ref contains a reference to the widget. Having this reference allows the hook to update the widget's position by setting its position to absolute and changing its left and top CSS properties. To calculate the widget's position (with respect to its top-left corner): To listen for dragging events, register event listeners on the events mousedown , mousemove , mouseup and dragstart within useEffect to capture the new position of the mouse cursor and change the position of the widget using the above mentioned calculation. When the widget component is unmounted, the hook will unregister these event listeners to avoid memory leaks. The useDragDrop hook returns triggerRef , which the component can set to a reference of the element dedicated to triggering the drag event, which is the dark-gray bar. ( src/hooks/useDragDrop.js ) To create data visualizations, you must provide them with data. d3 provides a csv method from its d3-fetch submodule to fetch CSV data from a remote source and parse it into an array of objects, which each represent a row in the CSV data. While waiting for the data to be fetched, a flag should be set to tell the component that the data is being fetched. Once the data is fetched, the flag should be toggled to tell the component that the data is available and ready to be used for rendering the data visualization. The useCsv hook requires three arguments: url , formatRows , transformResponse . url represents the URL of the CSV data. formatRows is a function that performs a transformation on each row of the CSV data and returns the transformed row. transformResponse is an optional function that performs a transformation on the entirety of the CSV data. This hook initializes the values of the state variables data and isLoading to an empty array and true respectively. Both of these values are returned by the hook to be accessed by the component that calls it. When the hook is first called, the data has not yet been fetched, so to indicate that it is being fetched, isLoading is set to true . Within the useEffect hook, the hook fetches the CSV data. The fetchData memoized function calls the csv method and waits for the data to be received. Once received, this data is processed and the data state variable is set to this data. After the data state variable is set or an error is encountered while fetching the CSV data, set the isLoading state variable to false to indicate that the CSV data has either been fetched or failed to be fetched. When the csv method fails to fetch the CSV data, data remains set to an empty array, so the resulting visualization will be empty. ( src/hooks/useCsv.js ) When integrating D3 into React, it is recommended to use D3 to perform calculations and use React to render the visualizations. When creating an axis, plotting data points at specific coordinates within a scatterplot or determining the height of a bar within a bar chart, we need a scale function that maps values from the data ("domain") to pixel values within the SVG canvas ("range"). For the scale function to extrapolate an appropriate pixel value for a value that does not exist within the data, you must pick a D3 scale function that accurately depicts the relationship between both sets of values. For a scatterplot, continuous quantitative data should be based on a linear scale ( scaleLinear ). For a bar chart, categorical data should be based on an ordinal scale ( scaleOrdinal ). The useScale hook requires only one argument: params . params contains the values needed to create a scale. By default, by only specifying the data , accessorKey and range options, the hook constructs a linear continuous scale, which scatterplots commonly use for plotting data points and generating axes. The accessorKey determines which specific field's value should be extracted from each record of the data to serve as the scale's domain. The range specifies the values the domain should be mapped to. Alternatively, you can customize the scale by specifying a different scale function ( scaleFn ) or explicitly passing a domain ( domain ). To apply rounding and padding to the range of a band scale (used for charts with an ordinal/categorical dimension), specify the isRangeRound flag to add spacing between bands (bars in a bar chart). To avoid recreating the scale whenever an unrelated state/prop value changes in the component calling the useScale hook, memoize the scale with useMemo , and only recreate the scale when the value of the params argument changes. ( src/hooks/useScale.js ) To distinguish different categories in a visualization, map each category to a unique color. For example, the Iris data contains observations for multiple flower species. If the observations are plotted onto a scatterplot, then coloring each point helps to differentiate the observations by flower species. Visually, colors make it easy to identify patterns and clusters. To create a color scale that maps each category to a unique color, use an ordinal scale, which maps a set of discrete values to a set of visual attributes. This scale should accept the flower species as the domain and the colors as the range. Since we already have a custom hook for creating scales, we can call useScale within the useColorScale custom hook to generate this color scale by passing it a unique set of options. The useColorScale hook requires only one argument, but its fields are unpacked within the hook's signature: data , key and colors . key represents the field within a record that contains the categorical value. To obtain a list of categories, extract the categorical value from each record, pass this list of values to a Set to remove duplicates and convert the set back to an array with the Array.from method. These categories will be memoized to avoid recreating them whenever an unrelated state/prop value changes in the component calling the useColorScale hook. This hook not only returns a color scale, but also the list of categories in case you want to render a legend within the visualization. Explore the demo to see how these custom hooks are used in the <BarChart /> and <Scatterplot /> components. Try writing some custom hooks for your React applications!

Thumbnail Image of Tutorial Writing Custom React Hooks for D3 Visualizations