#pass function arguments in react onclick
Explore tagged Tumblr posts
Text
React onClick Call Function with Parameters
React is a popular JavaScript library for building user interfaces, and one of its key features is the ability to handle user interactions with events. In this article, we’ll explore how to pass parameters to a function when it’s called via an onClick event in React. in this article you will learn: Understanding React Event Handling Event Handling in Class Components Event Handling in…
View On WordPress
#arrow function arguments in react#avoid inline arrow functions in react#best practices for handling click events in react#event handlers and callbacks in react#higher-order components in react#javascript event delegation in react#memoization in react#optimizing performance in react applications#pass function arguments in react onclick#react class components and event listeners#react event propagation and bubbling#react onclick call function with parameters#synthetic event properties in react#using useCallback hook in react
0 notes
Text
Understanding React's Component-Based Architecture
Introduction
React JS, a powerful JavaScript library created by Facebook, revolutionized the way developers build user interfaces by introducing a component-based architecture. This architecture allows for building complex UIs from small, reusable pieces of code known as components. Understanding and leveraging this architecture is essential for creating scalable and maintainable web applications.
What is a Component?
In React, a component is a self-contained unit that encapsulates its structure, logic, and styling. Components can be thought of as the building blocks of a React application. They can be as simple as a button or as complex as an entire form. There are two types of components in React:
Functional Components: These are simple functions that take props as an argument and return React elements. They are also known as stateless React components because they do not manage any state.
const Greeting = (props) => { return
Hello, {props.name}!
; };
Class Components: These are ES6 classes that extend React.Component. They can hold and manage their own state, making them suitable for more complex components.
class Greeting extends React.Component { render() { return
Hello, {this.props.name}!
; } }
Benefits of Component-Based Architecture
Reusability: Components can be reused across different parts of an application, reducing redundancy and promoting DRY (Don't Repeat Yourself) principles.
Maintainability: Each component manages its own logic and styling, making the codebase easier to maintain and understand.
Testability: Components can be tested individually, leading to more robust and reliable code.
Separation of Concerns: By dividing the UI into distinct components, developers can separate concerns, making it easier to manage and develop complex applications.
Building a React Application with Components
When building a React application, the first step is to break down the UI into a hierarchy of components. For example, consider a simple application with a header, a sidebar, and a main content area.
App Component: The root component that includes the header, sidebar, and content components.
Header Component: Contains the navigation and branding of the application.
Sidebar Component: Holds the links to different sections of the application.
Content Component: Displays the main content based on the user’s interaction with the sidebar.
const App = () => { return ( ); };
const Header = () =>
My Application
;
const Sidebar = () => (
Home
About
Contact
);
const Content = () =>
Welcome to my application!;
Managing State and Props
State and props are fundamental concepts in React that enable dynamic and interactive UIs.
Props: For properties, props are read-only attributes passed from a parent component to a child component. They allow data to flow down the component tree.
const Greeting = (props) =>
Hello, {props.name}!
;
State: State is a data structure that holds information that may change over the lifetime of a component. Unlike props, state is managed within the component.
class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; }increment = () => { this.setState({ count: this.state.count + 1 }); }; render() { return ( <div> <p>Count: {this.state.count}</p> <button onClick={this.increment}>Increment</button> </div> ); }
}
Conclusion
React’s component-based architecture simplifies the process of building and managing user interfaces by breaking them down into manageable, reusable pieces. This approach not only enhances reusability and maintainability but also promotes a clean separation of concerns, making complex applications easier to develop and maintain. Developers can create robust, scalable, and efficient React applications by understanding and utilizing components, props, and state.
0 notes
Text
Decoding React's Building Blocks: A Comprehensive Dive into Understanding Components
How Do React Components Work?
A component in React is a reusable, self-contained building block that contains a portion of the user interface along with its functionality. Parts might be as basic as a button or as intricate as a whole page. By dividing the user interface into small, manageable chunks, components aim to improve the modularity and maintainability of the development process.
Functional Components
React components come in the most basic form as functional components. These are JavaScript functions that return React elements after accepting props, which is short for properties, as arguments. React 16.8 brought React Hooks, which gave functional components the ability to handle state and side effects, increasing their strength and adaptability.
jsx
import React from 'react';
const MyComponent = (props) => {
return <div>{props.message}</div>;
};
Class Components
The more traditional method of defining components in React is using class components, which are still in use today. They have access to lifecycle methods—functions that are called at various stages of a component's life—and can manage local state. But in contemporary React development, functional components have mainly taken the role of class components since the release of Hooks.
Jsx
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { message: 'Hello, React!' };
}
render() {
return <div>{this.state.message}</div>;
}
}
Props and State
Props and state are two essential concepts in React components.
Props
Props are React components' inputs. They enable data to be passed from the parent component to its offspring. Because props are immutable, a child component cannot change the props it inherits.
jsx
// Parent Component
import React from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
return <ChildComponent message="Hello from parent!" />;
};
// Child Component
const ChildComponent = (props) => {
return <div>{props.message}</div>;
};
State
State, which can vary over time as a result of user interactions or other causes, denotes the internal state of a component. Because state is modifiable, modifications to it cause the component to be rendered again.
jsx
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
Styling React Components
A key component of web development is styling, and React components provide a variety of methods for handling it. Inline styles, external stylesheet imports, and CSS-in-JS libraries like styled-components are the three ways you can apply styles directly. Every strategy has benefits, and the decision is frequently based on the demands of the project as well as individual preferences.
Inline Styles
React's inline styles let you use JavaScript objects to apply styles directly within the component. With this method, styling at the component level is possible and styles can be dynamically altered based on props or state.
jsx
const StyledComponent = () => {
const styles = {
color: 'blue',
fontSize: '16px',
};
return <div style={styles}>I'm styled with inline styles</div>;
};
External stylesheets
Additionally, you can apply styles to your React components by using external stylesheets. Apply class names in your component file after importing the stylesheet.
jsx
// styles.css
.myComponent {
color: green;
font-size: 18px; }
// Component file
import React from 'react';
import './styles.css';
const StyledComponent = () => {
return <div className="myComponent">I'm styled with an external stylesheet</div>;
};
Lifecycle of a Component
Class components have a lifespan that includes mounting, updating, and unmounting, among other stages. Developers can hook into these stages and run code at designated intervals by using lifecycle methods. Nevertheless, functional components can now employ useEffect to have lifecycle-like behavior thanks to the advent of Hooks.
In summary
The foundation of contemporary web development is made up of React components, which provide an effective and modular method of creating user interfaces. Components, whether functional or class-based, offer an organized method for handling and arranging user interface elements. Learning about props, state, and lifecycle as you go with React development will enable you to build dynamic, interactive online applications.
Credit – Vidyadhar Chavan
MetricsViews Pvt. Ltd.
MetricsViews specializes in building a solid DevOps strategy with cloud-native including AWS, GCP, Azure, Salesforce, and many more. We excel in microservice adoption, CI/CD, Orchestration, and Provisioning of Infrastructure - with Smart DevOps tools like Terraform, and CloudFormation on the cloud.
www.metricsviews.com
0 notes
Text
onClick not working React js
I am trying to call a function when a user clicks a div (using onClick in react). I don't need to pass any arguments at this moment, just need to call the function. I'm fairly new to react.js so apologies in advance for my ignorance. Thanks.
var Test = React.createClass({btnTapped: function(){ console.log('tapped!');},render: function() { var stationComponents = this.props.stations.map(function(station, index) { return <div onClick={btnTapped()}><img src="img/test.png" />{station}</div>; }); return <div>{stationComponents}</div>; }});var cards = ["amazon", "aeo", "aerie", "barnes", "bloomingdales", "bbw","bestbuy", "regal", "cvs", "ebay", "gyft", "itunes", "jcp", "panera", "staples", "walmart", "target", "sephora", "walgreens", "starbucks"];ReactDOM.render(<Test stations={cards} />, document.getElementById('test-div'));
https://codehunter.cc/a/reactjs/onclick-not-working-react-js
0 notes
Text
How to Use Closures in JavaScript – A Beginner's Guide
Closures are a confusing JavaScript concept to learn, because it's hard to see how they're actually used.
Unlike other concepts such as functions, variables, and objects, you don't always use closures conscientiously and directly. You don't say: Oh! Here I will use a closure as a solution.
But at the same time, you might have already used this concept a hundred times. Learning about closures is more about identifying when one is being used rather than learning a new concept.
What is a closure in JavaScript?
You have a closure when a function reads or modifies the value of a variable defined outside its context.
const value = 1 function doSomething() { let data = [1,2,3,4,5,6,7,8,9,10,11] return data.filter(item => item % value === 0) }
Here the function doSomething uses the variable value. But also the function item => item % value === 0 can then be written like this:
function(item){ return item % value === 0 }
You use the value of the variable value that was defined outside of the function itself.
Functions can access values out of context
As in the previous example, a function can access and use values that are defined outside its "body" or context, for example:
let count = 1 function counter() { console.log(count) } counter() // print 1 count = 2 counter() // print 2
This allows us to modify the value of the count variable from anywhere in the module. Then when the counter function is called, it will know how to use the current value.
Why do we use functions?
But why do we use functions in our programs? Certainly it is possible – difficult, but possible – to write a program without using functions we define. So why do we create proper functions?
Imagine a piece of code that does something wonderful, whatever, and is made up of X number of lines.
/* My wonderful piece of code */
Now suppose you must use this wonderful piece of code in various parts of your program, what would you do?.
The "natural" option is to put this piece of code together into a set that can be reusable, and that reusable set is what we call a function. Functions are the best way to reuse and share code within a program.
Now, you can use your function as many times as possible. And, ignoring some particular cases, calling your function N times is the same as writing that wonderful piece of code N times. It is a simple replacement.
But where is the closure?
Using the counter example, let's consider that as the wonderful piece of code.
let count = 1 function counter() { console.log(count) } counter() // print 1
Now, we want to reuse it in many parts, so we will "wrap" it in a function.
function wonderfulFunction() { let count = 1 function counter() { console.log(count) } counter() // print 1 }
Now what do we have? A function: counter that uses a value that was declared outside it count. And a value: count that was declared in the wonderfulFunction function scope but that is used inside the counter function.
That is, we have a function that uses a value that was declared outside its context: a closure.
Simple, isn't it? Now, what happens when the function wonderfulFunction is executed? What happens to the variable count and the function counter once the parent function is executed?
The variables and functions declared in its body "disappear" (garbage collector).
Now, let's modify the example a bit:
function wonderfulFunction() { let count = 1 function counter() { count++ console.log(count) } setInterval(counter, 2000) } wonderfulFunction()
What will happen now to the variable and function declared inside wonderfulFunction?
In this example, we tell the browser to run counter every 2 seconds. So the JavaScript engine must keep a reference to the function and also to the variable that is used by it. Even after the parent function wonderfulFunction finishes its execution cycle, the function counter and the value count will still "live".
This "effect" of having closures occurs because JavaScript supports the nesting of functions. Or in other words, functions are first class citizens in the language and you can use them like any other object: nested, passed as an argument, as a value of return, and so on.
What can I do with closures in JavaScript?
This is a technique that was used a lot in the ES5 days to implement the "module" design pattern (before this was natively supported). The idea is to "wrap" your module in a function that is immediately executed.
(function(arg1, arg2){ ... ... })(arg1, arg2)
This lets you use private variables that can only be used by the module itself within the function – that is, it's allowed to emulate the access modifiers.
const module = (function(){ function privateMethod () { } const privateValue = "something" return { get: privateValue, set: function(v) { privateValue = v } } })() var x = module() x.get() // "something" x.set("Another value") x.get() // "Another Value" x.privateValue //Error
Function Factory
Another design pattern implemented thanks to closures is the “Function Factory”. This is when functions create functions or objects, for example, a function that allows you to create user objects.
const createUser = ({ userName, avatar }) => ({ id: createID(), userName, avatar, changeUserName (userName) { this.userName = userName; return this; }, changeAvatar (url) { // execute some logic to retrieve avatar image const newAvatar = fetchAvatarFromUrl(url) this.avatar = newAvatar return this } }); console.log(createUser({ userName: 'Bender', avatar: 'bender.png' })); { "id":"17hakg9a7jas", "avatar": "bender.png", "userName": "Bender", "changeUsername": [Function changeUsername] "changeAvatar": [Function changeAvatar] } */c
And using this pattern you can implement an idea from functional programming called currying.
Currying
Currying is a design pattern (and a characteristic of some languages) where a function is immediately evaluated and returns a second function. This pattern lets you execute specialization and composition.
You create these "curried" functions using closures, defining and returning the inner function of the closure.
function multiply(a) { return function (b) { return function (c) { return a * b * c } } } let mc1 = multiply(1); let mc2 = mc1(2); let res = mc2(3); console.log(res); let res2 = multiply(1)(2)(3); console.log(res2);
These types of functions take a single value or argument and return another function that also receives an argument. It is a partial application of the arguments. It is also possible to rewrite this example using ES6.
let multiply = (a) => (b) => (c) => { return a * b * c; } let mc1 = multiply(1); let mc2 = mc1(2); let res = mc2(3); console.log(res); let res2 = multiply(1)(2)(3); console.log(res2);
Where can we apply currying? In composition, let's say you have a function that creates HTML elements.
function createElement(element){ const el = document.createElement(element) return function(content) { return el.textNode = content } } const bold = crearElement('b') const italic = createElement('i') const content = 'My content' const myElement = bold(italic(content)) // <b><i>My content</i></b>
Event Listeners
Another place you can use and apply closures is in event handlers using React.
Suppose you are using a third party library to render the items in your data collection. This library exposes a component called RenderItem that has only one available prop onClick. This prop does not receive any parameters and does not return a value.
Now, in your particular app, you require that when a user clicks on the item the app displays an alert with the item's title. But the onClick event that you have available does not accept arguments – so what can you do? Closures to the rescue:
// Closure // with es5 function onItemClick(title) { return function() { alert("Clicked " + title) } } // with es6 const onItemClick = title => () => alert(`Clcked ${title}`) return ( <Container> {items.map(item => { return ( <RenderItem onClick={onItemClick(item.title)}> <Title>{item.title}</Title> </RenderItem> ) })} </Container> )
In this simplified example we create a function that receives the title that you want to display and returns another function that meets the definition of the function that RenderItem receives as a prop.
Conclusion
You can develop an app without even knowing that you are using closures. But knowing that they exist and how they really work unlocks new possibilities when you're creating a solution.
Closures are one of those concepts that can be hard to understand when you're starting out. But once you know you're using them and understand them, it allows you to increase your tools and advance your career.
🐦 Follow me on Twitter ✉️ Join to the newsletter ❤️ Support my work
If you read this far, tweet to the author to show them you care.
0 notes
Link
I LOVE data visualizations. I love how a strong visualization combines the fields of art, science, mathematics, and humanities to tell a story, and I love the interactive and exploratory aspect of visualizations. However, in a lot of visualizations, the designer makes all/most of the decisions about what data to display, and how to display it to tell a specific story from a specific point of view.
But as a teacher, I prefer the visualizations that allow the user to make more of these choices themselves, guided by their own questions, seeking answers and further inquiry. So as I started my journey in creating data visualizations, I knew I needed to make something that would render the data based on user input, and that sparked the concept for my current project-in-progress, the COVID-19 World Tracker.
First, check out a quick video demo and skim through the README.md file for a quick tour of the project.
Designing and Planning User Interaction Goals
My design choices were centered around this guiding question:
How can I display data and options that will allow the user to generate open-ended questions and freely investigate those questions to better understand the COVID-19 outbreak?
My goal was to develop a simple platform in which users could easily explore and compare the COVID-19 outbreak on a global scale, using real-time global data as well as looking at historical data throughout the outbreak. The map visualization portion of the app allows for quick analysis of current case data, and allows users to explore possible correlations between case numbers, severity of cases, and how the data looks when overall population is taken into account. The bar chart portion of the app allows users to compare the evolution of the outbreak from the first reported case in each country, through selecting a "collection" of countries for comparison. The two halves of the app operate together - ideally, after exploration of the global map, a user would be curious to dig deeper and compare certain countries based on what they saw in the map.
The remainder of this post will explain how I combined the React.js user interface with D3.js to achieve this goal, allowing users to be more in control of the data they explore, the questions they ask, and the understandings they walk away with. This post will focus on the map visualization, while my next post will focus on the bar charts.
Exploring Real-Time Global Data Using the World Map Visualization
The Available Data
The map data was fetched from the Novel COVID API. I chose this API because it pulls data from a variety of reputable sources, and includes consistent current data for every country. Here is an example of the data entry for Afghanistan:
Creating Options Based on Available Data
First, this data is fetched using the useEffect() hook in the MapInput component (React.js).
I used the keys for each of the data points (cases, deaths, population, etc) to organize a series of user input buttons in the MapInput component. The component renders a series of buttons, which when clicked, call the DisplayMapData() function (D3.js) with arguments related to the specific data the user wants to display on the map. For example:
<button onClick={event => DisplayMapData(event.target.value, event.target.innerText, countryResults)} className="cases block" value="cases"> Total Cases </button>
With a little bit of css button styling and react-bootstrap Row-Col-Card formatting, I created the right sidebar menu of options:
Visualizing the Data Selected by the User
From here, the DisplayMapData(caseType, caseTitle, data) function uses the arguments passed down to parse the data by the user input (caseType) to plot only that selected data on the map, using each country's geographic coordinates supplied by the countryInfo object within the data object. This function first removes any data related to the last selection (country circles, the legend, and the title), and then renders that information for the most recently-selected caseType.
You can view the code for the DisplayMapData() function in my repository
Adding Additional Interactive Features
Finally, I built in additional interactions so that the user could further explore the map and the data. This included:
Zooming and Panning the map view
Highlighting country boundaries and displaying country names
Displaying country data when hovering over each data circle
Comparing Country-Specific Historical Data Using the Bar Chart Visualizations
Keep an eye out for Part 2 of this post, describing the Bar Chart Portion of the App.
Final Thoughts
As a novice programmer, this project has forced me to dramatically grow my skill set in the past two weeks. I have a lot more to learn about D3.js. I plan to continue practicing making different types of visualizations with complex data sets, while always keeping the user experience and opportunities for self-directed inquiry at the forefront of my designs.
This project is by no means complete, and I will share a link to the live project in a few weeks. In the meantime, keep an eye out for my weekly updates to see how the project evolves.
And if you made it this far - Thank you for reading!
0 notes
Text
In this post we are going to learn about how to use multi language in React. Here localization means changing language of text in our React App.
Have you ever wondered how websites supports multi language feature. It’s not a rocket science. It’s pretty easy. We are going to use a 3rd party package for multi language support in our app.
react-localization
We are going to use package named react-localization in our app to support multi language and the best thing about this is that it is just 2.4kb code.
The library uses the current interface language, then it loads and displays the strings matching the current interface locale or the default language (the first one if a match is not found) if a specific localization can’t be found.
It’s possible to force a language different from the interface one.
Installation
The installation of this package is pretty straight forward. You can install it using below command:-
npm install --save react-localization
How to use
To use this package you first need to import it.
import LocalizedStrings from "react-localization";
After importing we need to create a new instance of this and need to provide an object of our text.
let strings = new LocalizedStrings({ en: { how: "How do you want your egg today?" }, it: { how: "Come vuoi il tuo uovo oggi?" } });
After adding the text we need to use the text in our render function.
{strings.how}
and to change the language we need to use its helper function. Which gonna receive only one parameter i.e language code.
strings.setLanguage(“en”);
If you want to go more deep it also provides 3 more helper functions.
getLanguage() – to get the current displayed language
getInterfaceLanguage() – to get the current device interface language
formatString() – to format the passed string replacing its placeholders with the other arguments strings
import React, { useState } from "react"; import "./styles.css"; import LocalizedStrings from "react-localization"; let strings = new LocalizedStrings({ en: { how: "How do you want your egg today?" }, it: { how: "Come vuoi il tuo uovo oggi?" } }); export default function App() { const [lang, setLanguage] = useState("en"); strings.setLanguage(lang); return ( <div className="App"> <div>{strings.how}</div> <button onClick={() => { setLanguage("en"); }} > English </button> <button onClick={() => { setLanguage("it"); }} > Italian </button> </div> ); }
0 notes
Text
Dealing With Stale Props and States in React’s Functional Components
There’s one aspect of JavaScript that always has me pulling my hair: closures. I work with React a lot, and the overlap there is that they can sometimes be the cause of stale props and state. We’ll get into exactly what that means, but the trouble is that the data we use to build our UI can be totally wrong in unexpected ways, which is, you know, bad.
Stale props and states
Long story short: it’s when code that is executed asynchronously has a reference to a prop or state that is no longer fresh, and thus, the value it returns is not the latest one.
To be even more clear, let’s play around with the same stale reference example React has in its documentation.
function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }
(Live demo)
Nothing fancy here. We have a functional component named Counter. It keeps track of how many times the user has clicked one button and shows an alert that displays how many times that button was clicked when clicking another button. Try this:
Click the “Click me” button. You are going to see the click counter go up.
Now click the “Show alert”button. Three seconds should go by and then trigger an alert telling you how many times you clicked the “Click me” button.
Now, click the “Show alert” button again and quickly click the “Click me” button before it triggers the alert in three seconds.
See what happens? The count shown on the page and the count shown in the alert do not match. The number in the alert is not just some random number, though. That number is the value the count variable had in the moment the asynchronous function inside the setTimeout was defined, which is the moment the “Show alert” button is clicked.
That’s just how closures work. We’re not going to get into the specifics of them in this post, but here are some docs that cover them in greater detail.
Let’s focus on how we can avoid these stale references with our states and props.
React offers a tip on how to deal with stale dates and props in the same documentation where the example was pulled.
If you intentionally want to read the latest state from some asynchronous callback, you could keep it in a ref, mutate it, and read from it.
By keeping the value asynchronously in a ref, we can bypass stale references. If you need to know more about ref in functional components, React’s documentation has a lot more information.
So, that begs the question: How can we keep our props or state in a ref?
Let’s do it the dirty way first.
The dirty way to store props and state in a ref
We can easily create a ref using useRef() and use count as its initial value. Then, wherever the state is being updated, we set the ref.current property to the new value. Lastly, use ref.current instead of count in the asynchronous part of our code.
function Counter() { const [count, setCount] = useState(0); const ref = useRef(count); // Make a ref and give it the count function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + ref.current); // Use ref instead of count }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => { setCount(count + 1); ref.current = count + 1; // Update ref whenever the count changes }} > Click me </button> <button onClick={() => { handleAlertClick(); }} > Show alert </button> </div> ); }
(Live demo)
Go ahead and do the same as last time. Click “Show alert” and then click “Click me” before the alert is triggered in three seconds.
Now we have the latest value!
Here’s why it works. When the asynchronous callback function is defined inside setTimeout, it saves a reference to the variables it uses, which is count in this case. This way, when the state updates, React not only changes the value but the variable reference in memory is completely different as well.
This means that — even if the state’s value is non-primitive — the variable you are working with in your asynchronous callback is not the same in memory. An object that would typically keep its reference throughout different functions now has a different value.
How does using a ref solve this? If we take a quick look at React’s docs again, we find an interesting, but easy-to-miss, bit of information:
[…] useRef will give you the same ref object on every render.
It doesn’t matter what we do. Throughout the lifetime of your component, React will give us the exact same ref object in memory. Any callback, no matter when it’s defined or executed, is working with the same object. No more stale reference.
The cleaner way to store props and state in a ref
Let’s be honest… using a ref like that is an ugly fix. What happens if your state is being updated in a thousand different places? Now you have to change your code and manually update the ref in all those places. That’s a no-no.
We are going to make this more scalable by giving ref the value of the state automatically when the state changes.
Let’s start by getting rid of the manual change to the ref in the “Click me”button.
Next, we make a function called updateState that is called whenever we need to change the state. This function takes the new state as an argument and it sets the ref.current property to the new state and updates the state as well with that same value.
Finally, let’s substitute the original setCount function React gives us with the new updateState function where the state is being updated.
function Counter() { const [count, setCount] = useState(0); const ref = useRef(count); // Keeps the state and ref equal function updateState(newState) { ref.current = newState; setCount(newState); } function handleAlertClick() { ... } return ( <div> <p>You clicked {count} times</p> <button onClick={() => { // Use the created function instead of the manual update updateState(count + 1); }} > Click me </button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }
(Live demo)
Using a custom hook
The cleaner solution works just fine. It gets the job done just like the dirty solution, but only calls a single function to update the state and ref.
But guess what? We can do better. What if we need to add more states? What if we want to do this in other components too? Let’s take the state, ref and updateState function and make them truly portable. Custom hooks to the rescue!
Outside the Counter component, we are going to define a new function. Let’s name it useAsyncReference. (It can be named anything, really, but note that it’s common practice to name custom hooks with “use” as a prefix.) Our new hook will have a single parameter for now. We’ll call it value.
Our previous solution had the same information stored twice: once in the state and once in the ref. We are going to optimize that by keeping the value just in ref this time. In other words, we will create a ref and give it the value parameter as its initial value.
Right after the ref, we will make an updateState function that takes the new state and sets it to the ref.current property.
Lastly, we return an array with ref and the updateState function, very similar to what React does with useState.
function useAsyncReference(value) { const ref = useRef(value); function updateState(newState) { ref.current = newState; } return [ref, updateState]; } function Counter() { ... }
We are forgetting something! If we check the useRef documentation, we learn that updating a ref does not trigger a re-render. So, while ref has the updated value, we wouldn’t see the changes on screen. We need to force a re-render every time ref gets updated.
What we need is a fake state. The value doesn’t matter. It’s only going to be there to provoke the re-render. We can even ignore the state and only keep its update function. We are calling that update function forceRender and giving it an initial value of false.
Now, inside updateState, we force the re-render by calling forceRender and passing it a state different to the current one after setting ref.current to newState.
function useAsyncReference(value) { const ref = useRef(value); const [, forceRender] = useState(false); function updateState(newState) { ref.current = newState; forceRender(s => !s); } return [ref, updateState]; } function Counter() { ... }
Take whatever value it has and return the opposite. The state doesn’t really matter. We are merely changing it so React detects a change in state and re-renders the component.
Next, we can clean the Count component and remove the previously used useState, ref and updateState function, then implement the new hook. The first value of the returned array is the state in the form of a ref. We’ll keep calling it count, where the second value is the function to update the state/ref. We’ll continue calling it setCount.
We also have to change the references to the count since now that they all must be count.current. And we must call setCount instead of calling updateState.
function useAsyncReference(value) { ... } function Counter() { const [count, setCount] = useAsyncReference(0); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count.current); }, 3000); } return ( <div> <p>You clicked {count.current} times</p> <button onClick={() => { setCount(count.current + 1); }} > Click me </button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }
Making this work with props
We have a truly portable solution for our problem. But guess what… there’s still a little more to do. Specifically, we need to make the solution compatible with props.
Let’s take the “Show alert” button and handleAlertClick function to a new component outside the Counter component. We are gonna call it Alert and it’s going to take a single prop called count. This new component is going to show the count prop value we are passing it in an alert after a three second delay.
function useAsyncReference(value) { ... } function Alert({ count }) { function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count); }, 3000); } return <button onClick={handleAlertClick}>Show alert</button>; } function Counter() { ... }
In Counter, we’re swapping the “Show alert” button for the Alert component. We’ll pass count.current to the count prop.
function useAsyncReference(value) { ... } function Alert({ count }) { ... } function Counter() { const [count, setCount] = useAsyncReference(0); return ( <div> <p>You clicked {count.current} times</p> <button onClick={() => { setCount(count.current + 1); }} > Click me </button> <Alert count={count.current} /> </div> ); }
(Live demo)
Alright, time to run through the testing steps again. See? Even though we are using a safe reference to the count in Counter, the reference to the count prop in the Alert component is not asynchronously safe and our custom hook is not suitable to use with props… yet.
Lucky for us, the solution is fairly simple.
All we have to do is add a second parameter to our useAsyncReference hook named isProp, with false as the initial value. Just before we return the array with ref and updateState, we set up a condition. If isProp is true, we set the ref.current property to value and only return ref.
function useAsyncReference(value, isProp = false) { const ref = useRef(value); const [, forceRender] = useState(false); function updateState(newState) { ref.current = newState; forceRender(s => !s); } if (isProp) { ref.current = value; return ref; } return [ref, updateState]; } function Alert({ count }) { ... } function Counter() { ... }
Now let’s update Alert so that is uses the hook. Remember to pass true as a second argument to useAsyncReference since we are passing a prop and not a state.
function useAsyncReference(value) { ... } function Alert({ count }) { const asyncCount = useAsyncReference(count, true); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + asyncCount.current); }, 3000); } return <button onClick={handleAlertClick}>Show alert</button>; } function Counter() { ... }
(Live demo)
Give it another try. Now it works perfectly whether you use states or props.
One last thing…
There’s one last change I’d like to make. React’s useState docs tell us that React will bail out of a re-render if the new state is identical to the previous one. Our solution doesn’t do that. If we pass the current state again to the hook’s updateState function, we will force a re-render no matter what. Let’s change that.
Let’s put the body of updateState inside an if statement and execute it when ref.current is different than the new state. The comparison must be done with Object.is(), just like React does.
function useAsyncReference(value, isProp = false) { const ref = useRef(value); const [, forceRender] = useState(false); function updateState(newState) { if (!Object.is(ref.current, newState)) { ref.current = newState; forceRender(s => !s); } } if (isProp) { ref.current = value; return ref; } return [ref, updateState]; } function Alert({ count }) { ... } function Counter() { ... }
Now we are finally done!
React can sometimes seem like a black box that is full of little quirks. Those quirks might be daunting to deal with, like the one we just tackled. But if you are patient and enjoy being challenged, you’ll soon realize it’s an awesome framework and a pleasure to work with.
The post Dealing With Stale Props and States in React’s Functional Components appeared first on CSS-Tricks.
source https://css-tricks.com/dealing-with-stale-props-and-states-in-reacts-functional-components/
from WordPress https://ift.tt/2xT5S9c via IFTTT
0 notes
Text
Getting Started With The React Hooks API
About The Author
Shedrack Akintayo is a software engineer from Lagos, Nigeria, who has a love for community building, open source and creating content and tech for the next … More about Shedrack …
In this tutorial, you are going to learn and understand what React hooks are, the basic React Hooks that are available and also examples of how to write them for your React applications. In the process, you will also get to know about some additional hooks that were shipped with React 16.8 and also how to write your own custom React Hooks.
When React 16.8 was released officially in early February 2019, it shipped with an additional API that lets you use state and other features in React without writing a class. This additional API is called Hooks and they’re becoming popular in the React ecosystem, from open-sourced projects to being used in production applications.
React Hooks are completely opt-in which means that rewriting existing code is unecessary, they do not contain any breaking changes, and they’re available for use with the release of React 16.8. Some curious developers have been making use of the Hooks API even before it was released officially, but back then it was not stable and was only an experimental feature. Now it is stable and recommended for React developers to use.
Note: We won’t be talking about React or JavaScript in general. A good knowledge of ReactJS and JavaScript will come in handy as you work through this tutorial.
What Are React Hooks?
React Hooks are in-built functions that allow React developers to use state and lifecycle methods inside functional components, they also work together with existing code, so they can easily be adopted into a codebase. The way Hooks were pitched to the public was that they allow developers to use state in functional components but under the hood, Hooks are much more powerful than that. They allow React Developers to enjoy the following benefits:
Improved code reuse;
Better code composition;
Better defaults;
Sharing non-visual logic with the use of custom hooks;
Flexibility in moving up and down the components tree.
With React Hooks, developers get the power to use functional components for almost everything they need to do from just rendering UI to also handling state and also logic — which is pretty neat.
Motivation Behind The Release Of React Hooks
According to the ReactJS official documentation, the following are the motivation behind the release of React Hooks:
Reusing stateful logic between components is difficult. With Hooks, you can reuse logic between your components without changing their architecture or structure.
Complex components can be difficult to understand. When components become larger and carry out many operations, it becomes difficult to understand in the long run. Hooks solve this by allowing you separate a particular single component into various smaller functions based upon what pieces of this separated component are related (such as setting up a subscription or fetching data), rather than having to force a split based on lifecycle methods.
Classes are quite confusing. Classes are a hindrance to learning React properly; you would need to understand how this in JavaScript works which differs from other languages. React Hooks solves this problem by allowing developers to use the best of React features without having to use classes.
The Rules Of Hooks
There are two main rules that are strictly to be adhered to as stated by the React core team in which they outlined in the hooks proposal documentation.
Make sure to not use Hooks inside loops, conditions, or nested functions;
Only use Hooks from inside React Functions.
Basic React Hooks
There are 10 in-built hooks that was shipped with React 16.8 but the basic (commonly used) hooks include:
These are the 4 basic hooks that are commonly used by React developers that have adopted React Hooks into their codebases.
useState()
The useState() hook allows React developers to update, handle and manipulate state inside functional components without needing to convert it to a class component. Let’s use the code snippet below is a simple Age counter component and we will use it to explain the power and syntax of the useState() hook.
function App() { const [age, setAge] = useState(19); const handleClick = () => setAge(age + 1) return <div> I am {age} Years Old <div> <button onClick={handleClick}>Increase my age! </button> </div> </div> }
If you’ve noticed, our component looks pretty simple, concise and it’s now a functional component and also does not have the level of complexity that a class component would have.
The useState() hook receives an initial state as an argument and then returns, by making use of array destructuring in JavaScript, the two variables in the array can be named what. The first variable is the actual state, while the second variable is a function that is meant for updating the state by providing a new state.
Our finished React app (Large preview)
This is how our component should look when it is rendered in our React application. By clicking on the “Increase my Age” button, the state of the age will change and the component would work just like a class component with state.
useEffect()
The useEffect() hook accepts a function that would contain effectual code. In functional components, effects like mutations, subscriptions, timers, logging, and other effects are not allowed to be placed inside a functional component because doing so would lead to a lot of inconsistencies when the UI is rendered and also confusing bugs.
In using the useEffect() hook, the effectual function passed into it will execute right after the render has been displayed on the screen. Effects are basically peeked into the imperative way of building UIs that is quite different from React’s functional way.
By default, effects are executed mainly after the render has been completed, but you have the option to also fire them when certain values change.
The useEffect() hook mostly into play for side-effects that are usually used for interactions with the Browser/DOM API or external API-like data fetching or subscriptions. Also, if you are already familiar with how React lifecycle methods work, you can also think of useEffect() hook as component mounting, updating and unmounting — all combined in one function. It lets us replicate the lifecycle methods in functional components.
We will use the code snippets below to explain the most basic way that we can by using the useEffect() hook.
Step 1: Define The State Of Your Application
import React, {useState} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surname}</h3> </div> ); }; export default App
Just like we discussed in the previous section on how to use the useState() hook to handle state inside functional components, we used it in our code snippet to set the state for our app that renders my full name.
Step 2: Call The useEffect Hook
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({name: 'Shedrack', surname: 'Akintayo'}) }, [])//pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surame}</h3> </div> ); }; export default App
We have now imported the useEffect hook and also made use of the useEffect() function to set the state of our the name and surname property which is pretty neat and concise.
You may have noticed the useEffect hook in the second argument which is an empty array; this is because it contains a call to the setFullName which does not have a list of dependencies. Passing the second argument will prevent an infinite chain of updates (componentDidUpdate()) and it’ll also allow our useEffect() hook to act as a componentDidMount lifecycle method and render once without re-rendering on every change in the tree.
Our React app should now look like this:
React app using the useEffect Hook (Large preview)
We can also use change the title property of our application inside the useEffect() function by calling the setTitle() function, like so:
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({name: 'Shedrack', surname: 'Akintayo'}) setTitle({'My Full Name'}) //Set Title }, [])// pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surame}</h3> </div> ); }; export default App
Now after our application has rerendered, it now shows the new title.
Our finished project (Large preview)
useContext()
The useContext() hook accepts a context object, i.e the value that is returned from React.createContext, and then it returns the current context value for that context.
This hook gives functional components easy access to your React app context. Before the useContext hook was introduced you would need to set up a contextType or a <Consumer> to access your global state passed down from some provider in a class component.
Basically, the useContext hook works with the React Context API which is a way to share data deeply throughout your app without the need to manually pass your app props down through various levels. Now, the useContext() makes using Context a little easier.
The code snippets below will show how the Context API works and how the useContext Hook makes it better.
The Normal Way To Use The Context API
import React from "react"; import ReactDOM from "react-dom"; const NumberContext = React.createContext(); function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); } function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Let’s now break down the code snippet and explain each concept.
Below, we are creating a context called NumberContext. It is meant to return an object with two values: { Provider, Consumer }.
const NumberContext = React.createContext();
Then we use the Provider value that was returned from the NumberContext we created to make a particular value available to all the children.
function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); }
With that, we can use the Consumer value that was returned from the NumberContext we created to get the value we made available to all children. If you have noticed, this component did not get any props.
function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Note how we were able to get the value from the App component into the Display component by wrapping our content in a NumberContext.Consumer and using the render props method to retrieve the value and render it.
Everything works well and the render props method we used is a really good pattern for handling dynamic data, but in the long run, it does introduce some unnecessary nesting and confusion if you’re not used to it.
Using The useContext Method
To explain the useContext method we will rewrite the Display component using the useContext hook.
// import useContext (or we could write React.useContext) import React, { useContext } from 'react'; // old code goes here function Display() { const value = useContext(NumberContext); return <div>The answer is {value}.</div>; }
That’s all we need to do in order to display our value. Pretty neat, right? You call the useContext() hook and pass in the context object we created and we grab the value from it.
Note: Don’t forget that the argument that is passed to the useContext hook must be the context object itself and any component calling the useContext will always re-render when the context value changes.
useReducer()
The useReducer hook is used for handling complex states and transitions in state. It takes in a reducer function and also an initial state input; then, it returns the current state and also a dispatch function as output by the means of array destructuring.
The code below is the proper syntax for using the useReducer hook.
const [state, dispatch] = useReducer(reducer, initialArg, init);
It is sort of an alternative to the useState hook; it is usually preferable to useState when you have complex state logic that has to do with multiple sub-values or when the next state is dependent on the previous one.
Other React Hooks Available
useCallback This hook returns a callback function that is memoized and that only changes if one dependency in the dependency tree changes. useMemo This hook returns a memoized value, you can pass in a “create” function and also an array of dependencies. The value it returns will only use the memoized value again if one of the dependencies in the dependency tree changes. useRef This hook returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will be available for the full lifetime of the component. useImperativeHandle This hook is used for customizing the instance value that is made available for parent components when using refs in React. useLayoutEffect This hook similar to the useEffect hook, however, it fires synchronously after all DOM mutations. It also renders in the same way as componentDidUpdate and componentDidMount. useDebugValue This hook can be used to display a label for custom hooks in the React Dev Tools. It is very useful for debugging with the React Dev Tools.
Custom React Hooks
A “custom Hook” is a JavaScript function whose names are prefixed with the word use and can be used to call other Hooks. It also lets you to extract component logic into reusable functions; they are normal JavaScript functions that can make use of other Hooks inside of it, and also contain a common stateful logic that can be made use of within multiple components.
The code snippets below demonstrate an example of a custom React Hook for implementing infinite scroll (by Paulo Levy):
import { useState } from "react"; export const useInfiniteScroll = (start = 30, pace = 10) => { const [limit, setLimit] = useState(start); window.onscroll = () => { if ( window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight ) { setLimit(limit + pace); } }; return limit; };
This custom Hook accepts two arguments which are start and pace. The start argument is the starting number of elements to be rendered while the pace argument is the subsequent number of elements that are to be rendered. By default, the start and pace arguments are set to 30 and 10 respectively which means you can actually call the Hook without any arguments and those default values will be used instead.
So in order to use this Hook within a React app, we would use it with an online API that returns ‘fake’ data:
import React, { useState, useEffect } from "react"; import { useInfiniteScroll } from "./useInfiniteScroll"; const App = () => { let infiniteScroll = useInfiniteScroll(); const [tableContent, setTableContent] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/todos/") .then(response => response.json()) .then(json => setTableContent(json)); }, []); return ( <div style=> <table> <thead> <tr> <th>User ID</th> <th>Title</th> </tr> </thead> <tbody> {tableContent.slice(0, infiniteScroll).map(content => { return ( <tr key={content.id}> <td style=>{content.userId}</td> <td style=>{content.title}</td> </tr> ); })} </tbody> </table> </div> ); }; export default App;
The code above will render a list of fake data (userID and title) that make use of the infinite scroll hook to display the initial number of data on the screen.
Conclusion
I hope you enjoyed working through this tutorial. You could always read more on React Hooks from the references below.
If you have any questions, you can leave them in the comments section and I’ll be happy to answer every single one!
The supporting repo for this article is available on Github.
Resources And Further Reading
(ks, ra, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/getting-started-with-the-react-hooks-api/ source https://scpie.tumblr.com/post/615094926681718784
0 notes
Text
Getting Started With The React Hooks API
About The Author
Shedrack Akintayo is a software engineer from Lagos, Nigeria, who has a love for community building, open source and creating content and tech for the next … More about Shedrack …
In this tutorial, you are going to learn and understand what React hooks are, the basic React Hooks that are available and also examples of how to write them for your React applications. In the process, you will also get to know about some additional hooks that were shipped with React 16.8 and also how to write your own custom React Hooks.
When React 16.8 was released officially in early February 2019, it shipped with an additional API that lets you use state and other features in React without writing a class. This additional API is called Hooks and they’re becoming popular in the React ecosystem, from open-sourced projects to being used in production applications.
React Hooks are completely opt-in which means that rewriting existing code is unecessary, they do not contain any breaking changes, and they’re available for use with the release of React 16.8. Some curious developers have been making use of the Hooks API even before it was released officially, but back then it was not stable and was only an experimental feature. Now it is stable and recommended for React developers to use.
Note: We won’t be talking about React or JavaScript in general. A good knowledge of ReactJS and JavaScript will come in handy as you work through this tutorial.
What Are React Hooks?
React Hooks are in-built functions that allow React developers to use state and lifecycle methods inside functional components, they also work together with existing code, so they can easily be adopted into a codebase. The way Hooks were pitched to the public was that they allow developers to use state in functional components but under the hood, Hooks are much more powerful than that. They allow React Developers to enjoy the following benefits:
Improved code reuse;
Better code composition;
Better defaults;
Sharing non-visual logic with the use of custom hooks;
Flexibility in moving up and down the components tree.
With React Hooks, developers get the power to use functional components for almost everything they need to do from just rendering UI to also handling state and also logic — which is pretty neat.
Motivation Behind The Release Of React Hooks
According to the ReactJS official documentation, the following are the motivation behind the release of React Hooks:
Reusing stateful logic between components is difficult. With Hooks, you can reuse logic between your components without changing their architecture or structure.
Complex components can be difficult to understand. When components become larger and carry out many operations, it becomes difficult to understand in the long run. Hooks solve this by allowing you separate a particular single component into various smaller functions based upon what pieces of this separated component are related (such as setting up a subscription or fetching data), rather than having to force a split based on lifecycle methods.
Classes are quite confusing. Classes are a hindrance to learning React properly; you would need to understand how this in JavaScript works which differs from other languages. React Hooks solves this problem by allowing developers to use the best of React features without having to use classes.
The Rules Of Hooks
There are two main rules that are strictly to be adhered to as stated by the React core team in which they outlined in the hooks proposal documentation.
Make sure to not use Hooks inside loops, conditions, or nested functions;
Only use Hooks from inside React Functions.
Basic React Hooks
There are 10 in-built hooks that was shipped with React 16.8 but the basic (commonly used) hooks include:
These are the 4 basic hooks that are commonly used by React developers that have adopted React Hooks into their codebases.
useState()
The useState() hook allows React developers to update, handle and manipulate state inside functional components without needing to convert it to a class component. Let’s use the code snippet below is a simple Age counter component and we will use it to explain the power and syntax of the useState() hook.
function App() { const [age, setAge] = useState(19); const handleClick = () => setAge(age + 1) return <div> I am {age} Years Old <div> <button onClick={handleClick}>Increase my age! </button> </div> </div> }
If you’ve noticed, our component looks pretty simple, concise and it’s now a functional component and also does not have the level of complexity that a class component would have.
The useState() hook receives an initial state as an argument and then returns, by making use of array destructuring in JavaScript, the two variables in the array can be named what. The first variable is the actual state, while the second variable is a function that is meant for updating the state by providing a new state.
Our finished React app (Large preview)
This is how our component should look when it is rendered in our React application. By clicking on the “Increase my Age” button, the state of the age will change and the component would work just like a class component with state.
useEffect()
The useEffect() hook accepts a function that would contain effectual code. In functional components, effects like mutations, subscriptions, timers, logging, and other effects are not allowed to be placed inside a functional component because doing so would lead to a lot of inconsistencies when the UI is rendered and also confusing bugs.
In using the useEffect() hook, the effectual function passed into it will execute right after the render has been displayed on the screen. Effects are basically peeked into the imperative way of building UIs that is quite different from React’s functional way.
By default, effects are executed mainly after the render has been completed, but you have the option to also fire them when certain values change.
The useEffect() hook mostly into play for side-effects that are usually used for interactions with the Browser/DOM API or external API-like data fetching or subscriptions. Also, if you are already familiar with how React lifecycle methods work, you can also think of useEffect() hook as component mounting, updating and unmounting — all combined in one function. It lets us replicate the lifecycle methods in functional components.
We will use the code snippets below to explain the most basic way that we can by using the useEffect() hook.
Step 1: Define The State Of Your Application
import React, {useState} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surname}</h3> </div> ); }; export default App
Just like we discussed in the previous section on how to use the useState() hook to handle state inside functional components, we used it in our code snippet to set the state for our app that renders my full name.
Step 2: Call The useEffect Hook
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({name: 'Shedrack', surname: 'Akintayo'}) }, [])//pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surame}</h3> </div> ); }; export default App
We have now imported the useEffect hook and also made use of the useEffect() function to set the state of our the name and surname property which is pretty neat and concise.
You may have noticed the useEffect hook in the second argument which is an empty array; this is because it contains a call to the setFullName which does not have a list of dependencies. Passing the second argument will prevent an infinite chain of updates (componentDidUpdate()) and it’ll also allow our useEffect() hook to act as a componentDidMount lifecycle method and render once without re-rendering on every change in the tree.
Our React app should now look like this:
React app using the useEffect Hook (Large preview)
We can also use change the title property of our application inside the useEffect() function by calling the setTitle() function, like so:
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({name: 'Shedrack', surname: 'Akintayo'}) setTitle({'My Full Name'}) //Set Title }, [])// pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surame}</h3> </div> ); }; export default App
Now after our application has rerendered, it now shows the new title.
Our finished project (Large preview)
useContext()
The useContext() hook accepts a context object, i.e the value that is returned from React.createContext, and then it returns the current context value for that context.
This hook gives functional components easy access to your React app context. Before the useContext hook was introduced you would need to set up a contextType or a <Consumer> to access your global state passed down from some provider in a class component.
Basically, the useContext hook works with the React Context API which is a way to share data deeply throughout your app without the need to manually pass your app props down through various levels. Now, the useContext() makes using Context a little easier.
The code snippets below will show how the Context API works and how the useContext Hook makes it better.
The Normal Way To Use The Context API
import React from "react"; import ReactDOM from "react-dom"; const NumberContext = React.createContext(); function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); } function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Let’s now break down the code snippet and explain each concept.
Below, we are creating a context called NumberContext. It is meant to return an object with two values: { Provider, Consumer }.
const NumberContext = React.createContext();
Then we use the Provider value that was returned from the NumberContext we created to make a particular value available to all the children.
function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); }
With that, we can use the Consumer value that was returned from the NumberContext we created to get the value we made available to all children. If you have noticed, this component did not get any props.
function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Note how we were able to get the value from the App component into the Display component by wrapping our content in a NumberContext.Consumer and using the render props method to retrieve the value and render it.
Everything works well and the render props method we used is a really good pattern for handling dynamic data, but in the long run, it does introduce some unnecessary nesting and confusion if you’re not used to it.
Using The useContext Method
To explain the useContext method we will rewrite the Display component using the useContext hook.
// import useContext (or we could write React.useContext) import React, { useContext } from 'react'; // old code goes here function Display() { const value = useContext(NumberContext); return <div>The answer is {value}.</div>; }
That’s all we need to do in order to display our value. Pretty neat, right? You call the useContext() hook and pass in the context object we created and we grab the value from it.
Note: Don’t forget that the argument that is passed to the useContext hook must be the context object itself and any component calling the useContext will always re-render when the context value changes.
useReducer()
The useReducer hook is used for handling complex states and transitions in state. It takes in a reducer function and also an initial state input; then, it returns the current state and also a dispatch function as output by the means of array destructuring.
The code below is the proper syntax for using the useReducer hook.
const [state, dispatch] = useReducer(reducer, initialArg, init);
It is sort of an alternative to the useState hook; it is usually preferable to useState when you have complex state logic that has to do with multiple sub-values or when the next state is dependent on the previous one.
Other React Hooks Available
useCallbackThis hook returns a callback function that is memoized and that only changes if one dependency in the dependency tree changes.useMemoThis hook returns a memoized value, you can pass in a “create” function and also an array of dependencies. The value it returns will only use the memoized value again if one of the dependencies in the dependency tree changes.useRefThis hook returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will be available for the full lifetime of the component.useImperativeHandleThis hook is used for customizing the instance value that is made available for parent components when using refs in React.useLayoutEffectThis hook similar to the useEffect hook, however, it fires synchronously after all DOM mutations. It also renders in the same way as componentDidUpdate and componentDidMount.useDebugValueThis hook can be used to display a label for custom hooks in the React Dev Tools. It is very useful for debugging with the React Dev Tools.
Custom React Hooks
A “custom Hook” is a JavaScript function whose names are prefixed with the word use and can be used to call other Hooks. It also lets you to extract component logic into reusable functions; they are normal JavaScript functions that can make use of other Hooks inside of it, and also contain a common stateful logic that can be made use of within multiple components.
The code snippets below demonstrate an example of a custom React Hook for implementing infinite scroll (by Paulo Levy):
import { useState } from "react"; export const useInfiniteScroll = (start = 30, pace = 10) => { const [limit, setLimit] = useState(start); window.onscroll = () => { if ( window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight ) { setLimit(limit + pace); } }; return limit; };
This custom Hook accepts two arguments which are start and pace. The start argument is the starting number of elements to be rendered while the pace argument is the subsequent number of elements that are to be rendered. By default, the start and pace arguments are set to 30 and 10 respectively which means you can actually call the Hook without any arguments and those default values will be used instead.
So in order to use this Hook within a React app, we would use it with an online API that returns ‘fake’ data:
import React, { useState, useEffect } from "react"; import { useInfiniteScroll } from "./useInfiniteScroll"; const App = () => { let infiniteScroll = useInfiniteScroll(); const [tableContent, setTableContent] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/todos/") .then(response => response.json()) .then(json => setTableContent(json)); }, []); return ( <div style=> <table> <thead> <tr> <th>User ID</th> <th>Title</th> </tr> </thead> <tbody> {tableContent.slice(0, infiniteScroll).map(content => { return ( <tr key={content.id}> <td style=>{content.userId}</td> <td style=>{content.title}</td> </tr> ); })} </tbody> </table> </div> ); }; export default App;
The code above will render a list of fake data (userID and title) that make use of the infinite scroll hook to display the initial number of data on the screen.
Conclusion
I hope you enjoyed working through this tutorial. You could always read more on React Hooks from the references below.
If you have any questions, you can leave them in the comments section and I’ll be happy to answer every single one!
The supporting repo for this article is available on Github.
Resources And Further Reading
(ks, ra, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/getting-started-with-the-react-hooks-api/ source https://scpie1.blogspot.com/2020/04/getting-started-with-react-hooks-api.html
0 notes
Text
Getting Started With The React Hooks API
About The Author
Shedrack Akintayo is a software engineer from Lagos, Nigeria, who has a love for community building, open source and creating content and tech for the next … More about Shedrack …
In this tutorial, you are going to learn and understand what React hooks are, the basic React Hooks that are available and also examples of how to write them for your React applications. In the process, you will also get to know about some additional hooks that were shipped with React 16.8 and also how to write your own custom React Hooks.
When React 16.8 was released officially in early February 2019, it shipped with an additional API that lets you use state and other features in React without writing a class. This additional API is called Hooks and they’re becoming popular in the React ecosystem, from open-sourced projects to being used in production applications.
React Hooks are completely opt-in which means that rewriting existing code is unecessary, they do not contain any breaking changes, and they’re available for use with the release of React 16.8. Some curious developers have been making use of the Hooks API even before it was released officially, but back then it was not stable and was only an experimental feature. Now it is stable and recommended for React developers to use.
Note: We won’t be talking about React or JavaScript in general. A good knowledge of ReactJS and JavaScript will come in handy as you work through this tutorial.
What Are React Hooks?
React Hooks are in-built functions that allow React developers to use state and lifecycle methods inside functional components, they also work together with existing code, so they can easily be adopted into a codebase. The way Hooks were pitched to the public was that they allow developers to use state in functional components but under the hood, Hooks are much more powerful than that. They allow React Developers to enjoy the following benefits:
Improved code reuse;
Better code composition;
Better defaults;
Sharing non-visual logic with the use of custom hooks;
Flexibility in moving up and down the components tree.
With React Hooks, developers get the power to use functional components for almost everything they need to do from just rendering UI to also handling state and also logic — which is pretty neat.
Motivation Behind The Release Of React Hooks
According to the ReactJS official documentation, the following are the motivation behind the release of React Hooks:
Reusing stateful logic between components is difficult. With Hooks, you can reuse logic between your components without changing their architecture or structure.
Complex components can be difficult to understand. When components become larger and carry out many operations, it becomes difficult to understand in the long run. Hooks solve this by allowing you separate a particular single component into various smaller functions based upon what pieces of this separated component are related (such as setting up a subscription or fetching data), rather than having to force a split based on lifecycle methods.
Classes are quite confusing. Classes are a hindrance to learning React properly; you would need to understand how this in JavaScript works which differs from other languages. React Hooks solves this problem by allowing developers to use the best of React features without having to use classes.
The Rules Of Hooks
There are two main rules that are strictly to be adhered to as stated by the React core team in which they outlined in the hooks proposal documentation.
Make sure to not use Hooks inside loops, conditions, or nested functions;
Only use Hooks from inside React Functions.
Basic React Hooks
There are 10 in-built hooks that was shipped with React 16.8 but the basic (commonly used) hooks include:
These are the 4 basic hooks that are commonly used by React developers that have adopted React Hooks into their codebases.
useState()
The useState() hook allows React developers to update, handle and manipulate state inside functional components without needing to convert it to a class component. Let’s use the code snippet below is a simple Age counter component and we will use it to explain the power and syntax of the useState() hook.
function App() { const [age, setAge] = useState(19); const handleClick = () => setAge(age + 1) return <div> I am {age} Years Old <div> <button onClick={handleClick}>Increase my age! </button> </div> </div> }
If you’ve noticed, our component looks pretty simple, concise and it’s now a functional component and also does not have the level of complexity that a class component would have.
The useState() hook receives an initial state as an argument and then returns, by making use of array destructuring in JavaScript, the two variables in the array can be named what. The first variable is the actual state, while the second variable is a function that is meant for updating the state by providing a new state.
Our finished React app (Large preview)
This is how our component should look when it is rendered in our React application. By clicking on the “Increase my Age” button, the state of the age will change and the component would work just like a class component with state.
useEffect()
The useEffect() hook accepts a function that would contain effectual code. In functional components, effects like mutations, subscriptions, timers, logging, and other effects are not allowed to be placed inside a functional component because doing so would lead to a lot of inconsistencies when the UI is rendered and also confusing bugs.
In using the useEffect() hook, the effectual function passed into it will execute right after the render has been displayed on the screen. Effects are basically peeked into the imperative way of building UIs that is quite different from React’s functional way.
By default, effects are executed mainly after the render has been completed, but you have the option to also fire them when certain values change.
The useEffect() hook mostly into play for side-effects that are usually used for interactions with the Browser/DOM API or external API-like data fetching or subscriptions. Also, if you are already familiar with how React lifecycle methods work, you can also think of useEffect() hook as component mounting, updating and unmounting — all combined in one function. It lets us replicate the lifecycle methods in functional components.
We will use the code snippets below to explain the most basic way that we can by using the useEffect() hook.
Step 1: Define The State Of Your Application
import React, {useState} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surname}</h3> </div> ); }; export default App
Just like we discussed in the previous section on how to use the useState() hook to handle state inside functional components, we used it in our code snippet to set the state for our app that renders my full name.
Step 2: Call The useEffect Hook
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({name: 'Shedrack', surname: 'Akintayo'}) }, [])//pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surame}</h3> </div> ); }; export default App
We have now imported the useEffect hook and also made use of the useEffect() function to set the state of our the name and surname property which is pretty neat and concise.
You may have noticed the useEffect hook in the second argument which is an empty array; this is because it contains a call to the setFullName which does not have a list of dependencies. Passing the second argument will prevent an infinite chain of updates (componentDidUpdate()) and it’ll also allow our useEffect() hook to act as a componentDidMount lifecycle method and render once without re-rendering on every change in the tree.
Our React app should now look like this:
React app using the useEffect Hook (Large preview)
We can also use change the title property of our application inside the useEffect() function by calling the setTitle() function, like so:
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({name: 'Shedrack', surname: 'Akintayo'}) setTitle({'My Full Name'}) //Set Title }, [])// pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surame}</h3> </div> ); }; export default App
Now after our application has rerendered, it now shows the new title.
Our finished project (Large preview)
useContext()
The useContext() hook accepts a context object, i.e the value that is returned from React.createContext, and then it returns the current context value for that context.
This hook gives functional components easy access to your React app context. Before the useContext hook was introduced you would need to set up a contextType or a <Consumer> to access your global state passed down from some provider in a class component.
Basically, the useContext hook works with the React Context API which is a way to share data deeply throughout your app without the need to manually pass your app props down through various levels. Now, the useContext() makes using Context a little easier.
The code snippets below will show how the Context API works and how the useContext Hook makes it better.
The Normal Way To Use The Context API
import React from "react"; import ReactDOM from "react-dom"; const NumberContext = React.createContext(); function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); } function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Let’s now break down the code snippet and explain each concept.
Below, we are creating a context called NumberContext. It is meant to return an object with two values: { Provider, Consumer }.
const NumberContext = React.createContext();
Then we use the Provider value that was returned from the NumberContext we created to make a particular value available to all the children.
function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); }
With that, we can use the Consumer value that was returned from the NumberContext we created to get the value we made available to all children. If you have noticed, this component did not get any props.
function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Note how we were able to get the value from the App component into the Display component by wrapping our content in a NumberContext.Consumer and using the render props method to retrieve the value and render it.
Everything works well and the render props method we used is a really good pattern for handling dynamic data, but in the long run, it does introduce some unnecessary nesting and confusion if you’re not used to it.
Using The useContext Method
To explain the useContext method we will rewrite the Display component using the useContext hook.
// import useContext (or we could write React.useContext) import React, { useContext } from 'react'; // old code goes here function Display() { const value = useContext(NumberContext); return <div>The answer is {value}.</div>; }
That’s all we need to do in order to display our value. Pretty neat, right? You call the useContext() hook and pass in the context object we created and we grab the value from it.
Note: Don’t forget that the argument that is passed to the useContext hook must be the context object itself and any component calling the useContext will always re-render when the context value changes.
useReducer()
The useReducer hook is used for handling complex states and transitions in state. It takes in a reducer function and also an initial state input; then, it returns the current state and also a dispatch function as output by the means of array destructuring.
The code below is the proper syntax for using the useReducer hook.
const [state, dispatch] = useReducer(reducer, initialArg, init);
It is sort of an alternative to the useState hook; it is usually preferable to useState when you have complex state logic that has to do with multiple sub-values or when the next state is dependent on the previous one.
Other React Hooks Available
useCallback This hook returns a callback function that is memoized and that only changes if one dependency in the dependency tree changes. useMemo This hook returns a memoized value, you can pass in a “create” function and also an array of dependencies. The value it returns will only use the memoized value again if one of the dependencies in the dependency tree changes. useRef This hook returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will be available for the full lifetime of the component. useImperativeHandle This hook is used for customizing the instance value that is made available for parent components when using refs in React. useLayoutEffect This hook similar to the useEffect hook, however, it fires synchronously after all DOM mutations. It also renders in the same way as componentDidUpdate and componentDidMount. useDebugValue This hook can be used to display a label for custom hooks in the React Dev Tools. It is very useful for debugging with the React Dev Tools.
Custom React Hooks
A “custom Hook” is a JavaScript function whose names are prefixed with the word use and can be used to call other Hooks. It also lets you to extract component logic into reusable functions; they are normal JavaScript functions that can make use of other Hooks inside of it, and also contain a common stateful logic that can be made use of within multiple components.
The code snippets below demonstrate an example of a custom React Hook for implementing infinite scroll (by Paulo Levy):
import { useState } from "react"; export const useInfiniteScroll = (start = 30, pace = 10) => { const [limit, setLimit] = useState(start); window.onscroll = () => { if ( window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight ) { setLimit(limit + pace); } }; return limit; };
This custom Hook accepts two arguments which are start and pace. The start argument is the starting number of elements to be rendered while the pace argument is the subsequent number of elements that are to be rendered. By default, the start and pace arguments are set to 30 and 10 respectively which means you can actually call the Hook without any arguments and those default values will be used instead.
So in order to use this Hook within a React app, we would use it with an online API that returns ‘fake’ data:
import React, { useState, useEffect } from "react"; import { useInfiniteScroll } from "./useInfiniteScroll"; const App = () => { let infiniteScroll = useInfiniteScroll(); const [tableContent, setTableContent] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/todos/") .then(response => response.json()) .then(json => setTableContent(json)); }, []); return ( <div style=> <table> <thead> <tr> <th>User ID</th> <th>Title</th> </tr> </thead> <tbody> {tableContent.slice(0, infiniteScroll).map(content => { return ( <tr key={content.id}> <td style=>{content.userId}</td> <td style=>{content.title}</td> </tr> ); })} </tbody> </table> </div> ); }; export default App;
The code above will render a list of fake data (userID and title) that make use of the infinite scroll hook to display the initial number of data on the screen.
Conclusion
I hope you enjoyed working through this tutorial. You could always read more on React Hooks from the references below.
If you have any questions, you can leave them in the comments section and I’ll be happy to answer every single one!
The supporting repo for this article is available on Github.
Resources And Further Reading
(ks, ra, yk, il)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/getting-started-with-the-react-hooks-api/
0 notes
Text
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.
😉SiliconWebX | 🌐CSS-Tricks
0 notes
Link
SitePoint
In this post, we’re going to learn how to create a music player using some of the new features in React 16.
In implementing this music player, we’re going to learn about some of the changes in React 16. There are quite a few changes, so we won’t cover all of them, but we’ll cover the ones that are important and that you can implement today.
The complete source for this post is available on GitHub.
To start the app, download the code, cd into the project directory and type:
npm install npm start
State in a React Application
All React applications include a property called state that determines how and what components (and any data associated with those components) should be displayed.
Our music player has a state property that contains two important pieces of information: one variable that specifies whether the player is playing music — the playing boolean — and one variable that tracks the state of the current track — the currentTrackIndex variable.
this.state = { playing: false, currentTrackIndex: 0 };
What is State?
When we refer to a component’s state, we mean a snapshot of the instance of the component on the page.
React’s components can define their own state, which we’ll use in this post. When we use state in a React component, the component is said to be stateful. A React component can define its own state using a state property for handling stateful components, such as our music player.
As the user clicks the play, pause, next, and previous buttons, and the tracks in the player, our component will update its current state.
Props vs State
For React applications, it’s important to understand the distinction between props and state. Our music player has two state variables that determine the way our application is displayed at a given point in time. The App component is our main component that drives the display of our child components — the Controls component and the TrackList component. In order for these two components to receive information about the state of our application, the App component will pass information down as props to the children components. These props can then be used in the child component to display their pieces of the application correctly. Another important thing to understand is that every time our App component updates, our Controls component and TrackList component will be updated as well, because they rely on information from the App component.
Controls
Our Controls component is the first child of our App component. The Controls component is given two props: onClick and playing. The onClick prop allows us to pass down our handleClick function we’ve defined in the App component to the Controls component. When the user clicks one of the buttons in our Controls component, the handleClick function will get called. The playing prop allows the Controls component to know what the current state of the player is so we can properly render the play icon or the pause icon.
Let’s explore how we render our buttons and handle clicks in our Controls component.
In our Controls component we have three important buttons:
The << (previous) button — an arrow icon pointing to the left — which selects the previous track in the list
The play/pause button which plays and pauses the music
The >> (next) button — an arrow icon pointing to the right — which selects the next track in the list.
When each of these buttons is clicked, we call the click handler function that we passed in from the App component. Each of the buttons in our music player application has an id which will aid us in determining how a particular click should be handled.
In the internals of the handleClick function, we use a switch statement that uses the id of the button that was clicked — e.target.id to determine how to handle the action from the button. In the next section, we’ll take a look at what happens in each case of the switch statement.
The play button
When the play button is clicked, we’ll need to update a few parts of our application. We’ll need to switch the play icon to the pause icon. We’ll also need to update the currentTrackIndex if it’s currently set to 0. In order to change these two parts of our application, we’ll call setState, a function available to every React component.
The setState function is available to all React components, and it’s how we update the state of our music player. The first argument in the setState function can either be an object or a function. If we’re not relying on the current state of an application to calculate the next state, using an object as the first argument is a perfectly fine approach and looks like this: this.setState({currentState:'newState'}). In our case, we’re relying on the current state of the application to determine the next state of our application, so we’ll want to use a function. The React documentation indicates why this is important:
React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
As React 16 turns on more of its features (including asynchronous rendering), this distinction will become more important to understand.
When the play button is clicked and we call setState, we pass in a function, because we’re relying on the current value of the currentTrackIndex state variable. The first argument that’s passed into the function is the previous state of our application, and the second argument is the current props. In our case, we just need the previous state of the application to determine the next state:
case "play": this.setState((state, props) => { let currentTrackIndex = state.currentTrackIndex; if (currentTrackIndex === 0) { currentTrackIndex = 1; }
Once we’ve set the currentTrackIndex properly based on the previous value of the currentTrackIndex, we then return an object of the values we want to update. In the case of the play button being clicked, we update our playing boolean to true and set the value of the currentTrackIndex:
return { playing: true, currentTrackIndex: currentTrackIndex };
The second argument that’s passed into the setState function is a callback function that’s called after the setState function is completed. When the play button is clicked, and the state of our application is updated, we want to start playing the music. We pass in the this.playAudio function as the second argument to our setState function.
},this.playAudio);
When the playAudio button is called, we reference the audio tag and call the load and play functions available to us via the Web Audio API.
playAudio(){ this.audioElement.load(); this.audioElement.play(); }
ref to a DOM element
In order to reference the actual audio DOM element to play the audio, we’ll need to use a special attribute available to all React components, the ref attribute. From the React documentation:
When the ref attribute is used on an HTML element, the ref callback receives the underlying DOM element as its argument.
In our situation, we add the ref attribute to our audio DOM element, and that allows us to play the audio for each track:
<audio ref={(audio)=>{this.audioElement = audio}} src={"/songs/"+this.state.currentTrackIndex+".mp3"}/>
The pause button
When the pause button is clicked, we call this.setState and set our playing boolean to false.
case "pause": this.setState({ playing: false },this.pauseAudio); break;
The second argument for our setState function call is our this.pauseAudio function, which references the audio element and calls the pause() function.
pauseAudio(){ this.audioElement.pause(); }
The << (previous) button
When the << icon is clicked, the id of the previous button matches the “prev” case of the switch statement, so the code associated with the “prev” case is executed. In the “prev” case, we call this.setState() again with a function like we did for playing and pausing our application. This time, we use the previous value of currentTrackIndex to decrement the value and return an object to set currentTrackIndex to the new value.
case "prev": this.setState((state, props) => { let currentIndex = state.currentTrackIndex - 1; if (currentIndex <= 0) { return null; } else { return { playing:true,currentTrackIndex: currentIndex }; } },this.playAudio);
Returning null from setState
One of the new changes in React 16 is that when we return null from a setState function, our application will not be re-rendered. Our track listing has 11 tracks available. If the user continues to click the << button, the currentTrackIndex will decrement until it gets to 0. Once it gets to 0, we no longer want to decrement the currentTrackIndex and we no longer need to re-render our application. We also do the same when our >> icon is clicked. If the currentTrackIndex is equal (or greater than) the number of tracks in our list (11), we return null from setState.
The >> (next) button
When the >> button is called, we have a similar functionality in place as the << button. Each time the user clicks >>, we increment the currentTrackIndex and we check that the currentTrackIndex is not greater than the length of the track list. If it is, we return null in our setState function call.
case "next": this.setState((state, props) => { let currentIndex = state.currentTrackIndex + 1; if (currentIndex > data.tracks.length) { return null; } else { return { playing:true,currentTrackIndex: currentIndex }; } },this.playAudio); break;
Recommended Courses
Wes Bos
A step-by-step training course to get you building real world React.js + Firebase apps and website components in a couple of afternoons. Use coupon code 'SITEPOINT' at checkout to get 25% off.
Track List
We’ve hard coded the track listing data in a JSON file for ease of understanding the concepts in this post. We import the data from the JSON file at the top, and in our lifecycle method componentDidMount, we set the state of our TrackList component. The state of our TrackList component contains one variable, the tracks variable.
Lifecyle methods componentDidMount and componentDidUpdate
Every React component, in addition to the setState function, also has lifecycle methods available. Our TrackList component uses two of these, componentDidMount and componentDidUpdate. componentDidMount is called when the React component is available in the DOM. In this case, we want to add some data to our component, so calling setState in componentDidMount is the appropriate time to do that.
When our App component updates the currentTrackIndex, the componentDidUpdate method in our TrackList component is triggered, because the TrackList component is getting new data. When the TrackList component gets new data, we want to make sure the currently selected track is in our viewport, so we make some calculations to determine where in the DOM the currently selected track exists and make it appear in the view of the track list container.
componentDidUpdate() { if (this.activeTrack) { let topOfTrackList = this.trackList.scrollTop; let bottomOfTrackList = this.trackList.scrollTop + this.trackList.clientHeight; let positionOfSelected = this.activeTrack.offsetTop; if ( topOfTrackList > positionOfSelected || bottomOfTrackList < positionOfSelected ) { this.trackList.scrollTop = positionOfSelected; } } }
Displaying the list of tracks
We use the JavaScript map function to loop over our array of tracks and call a function for each element in the array. The function we call is renderListItem, which contains some logic to determine if the currentTrackIndex is the current element in the array we’re rendering. If it is, we need to make sure the value for the className on the li includes the selected string. This will ensure that the styling for the selected track will be different when compared to the rest of the list.
renderListItem(track, i) { let trackClass = this.props.currentTrackIndex === track.id ? "selected" : ""; return ( <li key={track.id} className={trackClass} ref={cur => { if (this.props.currentTrackIndex === track.id) { this.activeTrack = cur; } }} onClick={()=>{this.props.selectTrackNumber(track.id)}} > <div className="number">{track.id}</div> <div className="title">{track.title}</div> <div className="duration">{track.duration}</div> </li> ); }
The li element also contains some other important attributes:
key: whenever we have a list, we need to include this attribute so that the list will render properly. For more information on using keys with lists in React, check out this article in the React documentation.
className: to make sure the li has the selected class attached to it if it’s the currently selected track.
ref: we use the ref attribute to calculate the correct location of the track list container. If the current track is not visible, we calculate the location of the current track and make it visible. We need to access the actual DOM element to make this calculation correctly.
onClick: when the user selects a particular track, we call this function, which calls this.props.selectTrackNumber. This function is passed into the TrackList component from our parent App component, just like the click handler for the Controls component. When this function is called, the state of our application is updated, with the currentTrackIndex getting set to the track number the user selected.
selectTrackNumber(trackId){ this.setState({currentTrackIndex:trackId,playing:true},this.playAudio); }
Try It Out!
Check out the Codepen example. The album art comes from an album by a band called the Glass Animals. Since we can’t legally stream the “Glass Animals” soundtrack, we’ve picked some royalty-free music to play in its place so we can get the full effect of the music player.
See the Pen React DailyUI – 009 – Music Player by Jack Oliver (@jackoliver) on CodePen.
This post is a part of the React Daily UI post series at Fullstack React, a joint effort between Jack Oliver, Sophia Shoemaker, and the rest of the team at Fullstack React.
Want to dive in deeper into React fundamentals? Check out Fullstack React: The Complete Guide to ReactJS & Friends to learn more.
Sophia Shoemaker has been building websites as a full stack developer since 2009. She's the creator of
Shopsifter
and the editor of the
Fullstack React
weekly newsletter.
http://j.mp/2f2Kxgt via SitePoint URL : http://j.mp/2c7PqoM
0 notes
Text
How to pass arguments to functions in React js?
I want to display person's email in the alert window. But, I do not know how to pass email as arguments to displayAlert method. Also, it wont let me use either. So, I have to assign displayAlert methos to a variable and use it in onClick. I do not know why it wont let me call it directly.
class People extends React.Component{render (){ var handleClick = this.displayAlert; var items = this.props.items.map(function(item) { return( <ul key = {item.id}> <li> <button onClick= {handleClick}>{item.lastName + ', ' + item.firstName}</button> </li> </ul> ) }); return (<div>{items}</div>); }displayAlert (){ alert('Hi');}} class PersonList extends React.Component{ render () { return ( <div> <People items={this.props.people}/> /* People is an array of people*/ </div> ); }}
https://codehunter.cc/a/reactjs/how-to-pass-arguments-to-functions-in-react-js
0 notes
Text
Getting Started with React - A modern, project-based guide for beginners (including hooks!)
So you want to learn React eh? This guide will walk through everything you need to know when getting started with React. We'll get set up, explain the "hows and whys" behind the basic concepts, and build a small project which pulls data from an API to see everything in action.
This will be a long one, so skip/re-read sections as you need using the "Jump to Section" links below. With that out of the way, grab a drink, buckle up, and let's get started.
Jump to Section
Prerequisites
You don't need to know any React before reading. There are a few things you will need to be familiar with if you want to get the most out of this getting started with React guide:
Basic JavaScript
React is a JavaScript library, so it makes sense to know JavaScript before learning React right? Don't worry, you won't need to know JavaScript inside out - you only need to know the basics:
Variables, functions, data types
Arrays and Objects
ES6 Syntax (using let & const, Arrow Functions, Destructuring Assignment, classes, importing/exporting, etc)
How JavaScript is used to manipulate the DOM
Basic HTML
In React, we use what's called JSX to create the HTML for our webpages. We'll explain JSX in depth later, for now make sure you have a good foundation when it comes to HTML:
How to structure HTML (how to nest elements and so on)
HTML attributes (i.e "id", "class", "onclick" and so on)
Subscribe to get my latest book "React-Ready JavaScript" which will help you get ramped up on the JavaScript you need before getting started with React!
Development Environment
The first thing we're going to do is set up a development environment. If you already setup Node.js and installed Visual Studio Code (or your preferred IDE), you can go ahead and skip to the next section [ADD LINK TO NEXT SECTION HERE]
Node.js
Go here and download the right package for your OS (Mac/windows etc)
When the installation completes, open a terminal and type this command:
node -v
This should show output the version of node you just installed:
This means that the node command works and node has installed successfully - hurray! If you see any errors, try reinstalling Node from the package you downloaded and retry the command again.
Visual Studio Code
Visual studio code is a popular open-source IDE that works well for frontend development. There are a bunch of others you can try - see what your favourite is and download that if you prefer. For now, we'll run with VS Code.
Click here and download the version for your platform:
Follow the installation steps, and you should be good to go. Go ahead and fire up Visual Studio Code.
That's enough development setup for now. There are other nice things you can install (VS Code extensions etc) but we don't need those right now -We're here to learn React!
Creating a React App
The next step is to create a React project. Lucky for us, the fine folk at Facebook have made this really simple. All we have to do is run a command within our terminal:
npx create-react-app my-app
This creates a project for us called "my-app" and sets everything up automatically. Pretty cool.
Go ahead and open up a terminal in the directory you want to create your app, e.g a "projects" folder, and run the command. Let the terminal do its thing, and after a while, this will complete and show you some commands:
Notice the create-react-app output has told us what we need to do to start the app. Go ahead and run the commands in your terminal:
cd my-app yarn start
This will start a development server and open up a web browser for you:
You've just set up your first React App! If you want to learn more about what's going on, (check out the "create-react-app" GitHub:)[https://github.com/facebook/create-react-app]
Exploring Create React App
Open up Visual Studio code (or whatever IDE you installed) and select File > Open… and select the my-app folder that was just created for us using create-react-app. This will open up our shiny new react app in the IDE, so we can write some code!
You should see the project structure to the right:
Look at all that stuff! Don’t worry too much about a lot of it, it’s mostly boilerplate code and config that we won’t be touching too much in this tutorial — phew! However since you’re a curious developer, let’s have a look at the project tree and see what we have:
Node Modules
This is where our packages go that we install through NPM (Node Package Manager). If you’re not familiar with NPM, it’s a glorious place where we can share code (usually open source) that other developers can use instead of writing their own.
Instead of using script tags like we do in traditional HTML, we install these modules as part of the application. Then, we use an import statement to access the code from that module. We’ll see this in action later.
Public Folder
This is where our bundled code goes. When we are ready to deploy our app, we run a ** build script**and the final files go in here. This will typically be our HTML, JavaScript, and CSS files. This is the folder we dump onto a web server somewhere, so that we can let users see our app via a URL
Index.html
The index.html is the entry point, or the first thing the web browser loads when a user navigates to the URL hosting our app.
If we look at the file, it’s a just a normal HTML file with normal HTML stuff that you will hopefully be familiar with. If we look at the body — it’s empty. React will dynamically convert our React code into HTML and load it here, in the “root” div.
With that out of the way, let’s look at the juicy parts — the code.
Our First Component
Open up App.js from the project tree. This is the Main component in our application. This is the first component to get rendered. It’s the “big cheese” of components.
The first thing we’re going to do in our big cheese component is delete everything, and build our very own component from scratch, to better understand what’s going on.
Now that we have a nice blank slate to play with we will start by importing react. This brings the React library into scope and gives us access to all the lovely features:
import React from "react";
Next we will declare a function. We’ll use ES6 arrow functions here. That’s more or less what a “component” is - a function with some logic and markup. We’re also going to export this function so we can use it elsewhere:
const App = () => { } export default App;
Within our function we want to write return(). This is what get’s returned from this component, and contains our markup which gets converted and rendered as HTML.
Finally let’s add a <div> with a <h1> title tag. Our finished component looks like this:
import React from "react"; const App = () => { return ( <div> <h1>Hello React World</h1> <h2> This is our first React App - isn't it marvellous?! </h2> </div> ); } export default App;
Now you’re probably thinking woah! HTML in a function? What is this madness? Even though it looks like HTML, it’s actually something called JSX (JavaScript XML). This basically allows us to mix JavaScript and HTML together.
This might seem a bit strange. We originally learned front end development by separating our HTML and JavaScript (and even CSS). Yet JavaScript and the way we design apps has evolved, and keeping everything together in the same “component” makes it easier to maintain and reuse our code.
Let’s see this in action. Open your terminal and run
npm start
This should open the browser and you should see the app running.
Congrats! You’ve just created your first component!
JSX
You probably have some question marks floating above your head when thinking about this JSX thing. Let’s take a deeper look into this.
return ( <div> <h1>Hello React World</h1> <h2> This is our first React App - isn't it marvellous?! </h2> </div> );
This looks like HTML, but it’s not. This is JSX! Even though it looks like normal HTML, what’s happening behind the scenes is that React is creating the element tree, using this syntax:
React.createElement(component, props, ...children)
component: The HTML element you wish to created, i.e h1, div etc
props: any props you wish to pass to that component (we’ll talk about props later)
children: An array of HTML elements that are nested within this element
So, the same component we have just created can be written as so:
const App = () => { return ( React.createElement( "div", null, React.createElement("h1", null, "Hello React World"), React.createElement( "h2", null, "This is our first React App - isn't it marvellous?!" ) ) ); }
Which looks a bit nasty (it was even nastier trying to type it out). If you trace through it carefully, you can see we are creating a div element, which has no props (indicated by passing null as a second argument). Lastly we are creating 2 more elements using the createElement syntax - our H1 and our H2 elements.
If you’ve been playing with JavaScript for a while, you might have noticed that this is similar to document.createElement. And it is! This is a JavaScript library after all!
This is the advantage of JSX in React - it lets us write HTML like syntax, without the messy React.createElement() stuff.
In the real world, React developers almost exclusively use JSX to write their code. No, this section wasn’t a waste of time - it’s always good to understand what happens under the hood. Knowledge is power (and less questions in my inbox)!
Making things dynamic
So we’ve seen JSX, and gotten over our fear of it (hopefully). But what’s the point? Why use this JSX thing, when we could just use HTML? They look the same? Right?
Good question my friend! Well, if we remember what JSX stands for - JavaScript XML. This means we can use JavaScript to make things dynamic. Our previous example looks like so:
const App = () => { return ( <div> <h1>Hello React World</h1> <h2>This is our first React App - isn't it marvellous?!</h2> </div> ); }
Now let’s say we want to make our text more dynamic. Firstly let’s add a variable to hold our message:
cont message = "This is my first variable rendered in JSX!"
Now to add JavaScript to this, we use ** curly braces**:
const App = () => { const message = "This is my first variable rendered in JSX!"; return ( <div> <h1>Hello React World</h1> <h2>{message}</h2> </div> ); }
If you run this in the browser, you’ll notice the text of our message variable appears. Go ahead and change the message variable text to something else and watch the magic happen.
We use curly braces to tell the compiler “execute this code as JavaScript”. If we didn’t have curly braces, the message variable wouldn't get executed as JavaScript and instead, the text “message” would appear on the screen. Try this out and see!
Handling Events
The same approach can be taken when with handling events. When using JSX, React gives us access to event listeners you may already be familiar with: onClick, onPress, onSubmit and so on.
Let’s say we want to display an alert when the message is clicked. Firstly, we add the onClick property to our h2 tag.
The onClick property accepts a function (in other words, we pass a function as an argument. This function will call the alert like so:
const App = () => { const message = "This is my first variable rendered in JSX!"; return ( <div> <h1>Hello React World</h1> <h2 onClick={()=> alert("you clicked the message!")}>{message}</h2> </div> ); }
Notice how we use a arrow function here to create a nice, concise inline function. If you’re not familiar with this syntax, make sure to checkout my book where I cover this and more here.
Again, notice how we have put this code within curly braces, to ensure the function gets executed as JavaScript.
Calling functions
So we looked at inline functions in the last example. Since JSX is JavaScript, we can create and reference functions outside of the return block. Our last example could look like this:
const App = () => { const message = "This is my first variable rendered in JSX!"; const handleClick = () =>{ alert("you clicked the message!"); } return ( <div> <h1>Hello React World</h1> <h2 onClick={handleClick}>{message}</h2> </div> ); }
Notice how we created a function called handleClick which alerts the message. Instead of using an inline function, we reference this function in our onClick property. Try this out and see what happens.
These are just some examples as to how we can use JavaScript to make things dynamic, and hopefully shows you the power of JSX. We’ll deepen our understandings later as we build out an example, so don't worry if some things don’t make sense just yet!
How a Component gets Rendered
Hopefully I’ve cleared up some of the questions you might have around JSX. The next thing you might be wondering is — how does a component get rendered? Where? When?
Let’s start at the beginning. If you look back to our file structure we have an index.js file. This is the first file to run (we often call this an “Entry Point”). This is typically by convention — you can change the entry point if you want, but for now we’ll leave it alone.
If we dig into the file, you’ll notice we have this line:
ReactDOM.render(<App />, document.getElementById("root"));
Notice we have document.getElementById(“root”) - finally some normal looking JavaScript! This gets the root element from the DOM using plain ol’ JavaScript, and renders our App Component within it. Our App component is imported like so:
import App from "./App"
Remember we exported our app component in App.js. This lets other files/components import and use our App component.
So where does the root element come from? Well, remember our index.html file in the public folder? This index.html file is the first HTML file to get loaded when the website loads
Within it we have a div with an ID of root, which is empty. This is where React loads our components. Let’s have a look at this in the dev tools.
Open up Chrome (or whatever browser you use) and inspect the dev tools. You’ll see somewhere in the tree a div with id=“root”, as well as the HTML rendered from our App component. Pretty cool!
Quick Summary
Before moving on, let’s quickly summarise what we’ve learned so far:
We have an index.html file, which is the skeleton of our web app
When the app starts, index.html loads, and imports our App Component
The JSX in the App component get’s converted to HTML, which is then rendered in the index.html file at the root div
Now that we have our feet wet with React, and have a better understanding of how things fit together, let’s build an example application using what we have learned so far. We’ll also learn some common React features that will help you well on to the road to getting started with React. Let’s go!
Our contacts list will display a number of a contacts, including their name, email, age and avatar (or, profile image). We’ll build this up gradually, eventually pulling data from an API. How exciting!
Get the styles
Since this is a React tutorial, we’re going to focus on the inner workings of React and not worry about creating nice styles. In your source folder, create a new file styles.css and paste in the following code:
.contact-card { display: flex; padding: 10px; color: #ffffff; background-color: rgb(42, 84, 104); font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; box-shadow: 10px 10px 25px -16px rgba(0, 0, 0, 0.75); border-radius: 10px; max-width: 500px; max-height: 125px; margin-bottom: 10px; } .contact-card p { margin-left: 10px; margin-top: 0; } button { margin-left: 10px; margin-bottom: 10px; }
Next, go into App.js and import the stylesheet like so:
import "./styles.css";
While we’re still in App.js, let’s add the basic JSX to get our layout for the contact card in place. Remove everything from the return statement and add the following:
<div className="contact-card"> <img src="https://via.placeholder.com/150" alt="profile" /> <div className="user-details"> <p>Name: Jenny Han</p> <p>Email: [email protected]</p> <p>Age: 25</p> </div> </div>
All we’re doing here is creating a div to “wrap” the contact card details, adding an image (the image will use a placeholder taken from the web for now), and adding a few p tags to hold the details we need in the contact card. Finally we’re adding some CSS classes taken from styles.css;
NOTE: to reference CSS classes, we need to use the className keyword. This is because we are writing JSX, and “class” is a reserved word in JavaScript.
Here’s what we have so far in our App.js file:
import React from "react"; import "./styles.css"; const App = () => { return ( <div className="contact-card"> <img src="https://via.placeholder.com/150" alt="profile" /> <div className="user-details"> <p>Name: Jenny Han</p> <p>Email: [email protected]</p> <p>Age: 25</p> </div> </div> ); }
If you run this in the browser, you should see something similar to the following:
OK so we have our contact card! However it’s not very reusable. We know that we are going to need to reuse this code if we want to render more than one card, so it makes sense to break this out into it’s own component
NOTE - To make it easier to follow, I am going to a put all the components we make into App.js . In the real world it would be better to split these different components into their own files, and import/export them where appropriate.
Just beneath the App function, create a new function called ContactCard, and copy the JSX from App to ContactCard like so:
const ContactCard = () => { return ( <div className="contact-card"> <img src="https://via.placeholder.com/150" alt="profile" /> <div className="user-details"> <p>Name: Jenny Han</p> <p>Email: [email protected]</p> <p>Age: 25</p> </div> </div> ); };
Again, a component in React is just a function that returns some JSX. Now that we’ve moved our JSX to the ContactCard we can use this component within our main App component:
const App = () => { return ( <> <ContactCard /> </> ); }
We use our own components like any old HTML/JSX tag. We just put the name of our component in angle brackets. Our App.js file should look like this:
// App.js import React from "react"; import "./styles.css"; const App = () => { return ( <> <ContactCard /> </> ); }; const ContactCard = () => { return ( <div className="contact-card"> <img src="https://via.placeholder.com/150" alt="profile" /> <div className="user-details"> <p>Name: Jenny Han</p> <p>Email: [email protected]</p> <p>Age: 25</p> </div> </div> ); };
Now if you run this in the browser, things will look the same as they did before - which is what we want. We now have a ContactCard component that we can use as many times as we like:
const App = () => { return ( <> <ContactCard /> <ContactCard /> <ContactCard /> </> ); };
Update the App component to include another 2 ContactCard components. The above example will render 3 contact cards in the browser. Go and check it out!
Think of this like a “stamp” on the page. Every ContactCard component we add is another “stamp” and renders the same markup on the page
Let’s talk about State - the useState Hook
If you’ve been getting started with React already, you may have heard of the term state. State is quite a big deal in React. So what is it?
State is basically an object that represents a part of an app that can change, which the UI “reacts” to. State can be anything; objects, booleans, arrays, strings or integers
Let’s take an example.
Some people who appear in our contact list are shy and do not want their age being displayed until a button is clicked. We can store whether the age should be shown or not in state by using the useState hook within the component. Which looks like this:
const [showAge, setShowAge] = useState(false);
“what the hell is going on here?” Let me explain.
The useState object gives us a variable with the current value, and a function that lets us change that value. When we call useState we can define an initialvalue (in this case, false).
We use destructuring assignment on the useState hook to get these. You don’t have to worry about destructuring assignment right now, just remember that the first variable lets us access the state value, the second one lets us change it.
Go ahead and add the above code snippet to the ContactCard component like so:
const ContactCard = () => { const [showAge, setShowAge] = useState(false); return ( <div className="contact-card"> <img src="https://via.placeholder.com/150" alt="profile" /> <div className="user-details"> <p>Name: Jenny Han</p> <p>Email: [email protected]</p> <p>Age: 25</p> </div> </div> ); };
Now we have a state object, how do we use it? Well, we can reference the showAge variable like any other variable. In this case, we want to _only show the age if the showAge variable is true.
We can do this using the ternary operator :
{showAge === true ? <p>Age: 25</p> : null}
This example reads as if the showAge variable is true, render the age, if not, render nothing.
Go ahead and add this to the ContactCard component, like so:
const ContactCard = () => { const [showAge, setShowAge] = useState(false); return ( <div className="contact-card"> <img src="https://via.placeholder.com/150" alt="profile" /> <div className="user-details"> <p>Name: Jenny Han</p> <p>Email: [email protected]</p> {showAge === true ? <p>Age: 25</p> : null} </div> </div> ); };
Now, if you run the app in the browser, you’ll see the age disappears - that’s because our showAge variable has been initialised with false. If we initialise our showAge variable with true:
const [showAge, setShowAge] = useState(true);
The age will appear on the contact card. Nice! Although, its not great — we don’t want to change the code whenever we want to show the age on the contact card!
Before we look at how to dynamically change our showAge variable, lets tidy the code a bit. Go ahead and replace this line:
{showAge === true ? <p>Age: 25</p> : null}
With:
{showAge && <p>Age: 25</p> }
This gives the same result, just in a more concise way.
TIP: Shorten code where it makes sense to, don’t feel like you have to shorten every line of code you write! Readability should come first.
Updating State
Ok back to updating state. If we remember back, the useState() hook gives us a function to update the state. Let’s wire this up to a button, which, when clicked, will toggle showing the age on the contact card.
We can do this with the following:
<button onClick={() => setShowAge(!showAge)}> Toggle Age </button>
What this is doing is calling the setShowAge function (which we get from the useState hook) to change the value of show age to the opposite of what it currently is.
NOTE: I’m using the Arrow Function syntax here to pass a function to the onClick property. If you’re not familiar we this, a quick reminder that you can get my [book where I discuss the important bits of JavaScript to know before React here].
When the state updates, React will re-render the component and since the value of showAge is true, the age will be displayed.
If the user clicks the button again, this will set showAge to false, React will re-render the component, and the age will be hidden:
Look at our fancy toggle in action!
TIP: Whenever the components state changes, React will re-render the component with the new state
Notice how even though we have 3 ContactCard components being rendered, when we click the button the age only displays for one of the cards, and not all of them. This is because state belongs to the individual component. In other words, each ContactCard component that renders is a copy, and has its own state/data.
Introducing Props
So now we have a lovely new ContactCard component that we’re reusing a few times. Although its not really reusable, since the name, email, age and avatar are the same for each of our components. Oh dear! We can make this data more dynamic with what are called props.
Since you’re just getting started with React, you can think ofProps as data that gets passed to a component, which the component can then use. For example, we can pass in our avatar , ** email**, name and age as props to our Contact Card component like so:
<ContactCard avatar="https://via.placeholder.com/150" name="Jenny Han" email="[email protected]" age={25} />
As you can see, we define a prop by giving it a name. Eg. name and using the equals to assign some value to that prop e.g Jenny Han.
We can have as many props as we want, and we can name these props whatever we want, so they’re pretty flexible.
Props can hold different types of data, i.e strings, numbers, booleans, objects, arrays and so on.
NOTE: Props must be defined using quoted text (e.g name=“Jenny Han”) or within braces (e.g age={25}. If we leave out the braces for anything other than strings things start to break - age=25 );
Go ahead and replace the current ContactCard components within our App component with the following:
<ContactCard avatar="https://via.placeholder.com/150" name="Jenny Han" email="[email protected]" age={25} /> <ContactCard avatar="https://via.placeholder.com/150" name="Jason Long" email="[email protected]" age={45} /> <ContactCard avatar="https://via.placeholder.com/150" name="Peter Pan" email="[email protected]" age={100} />
All we’re doing here is passing the data that the component needs to each component as props. Notice how the data is different for each component.
Using Props within a component
We’ve sent a bunch of props down to the ContactCard component, so let’s tell the ** ContactCard** how to use them.
Until now, our ** ContactCard** function doesn’t accept any parameters. React, being the magical thing that it is, automatically puts all our props into a lovely props object, that gets passed into the component:
const ContactCard = props => { //...other code };
Notice the props variable. This is an object containing the props we defined previously. We can access our defined props by using the dot notation like so:
const ContactCard = props => { console.log(props.avatar); console.log(props.name); console.log(props.email); console.log(props.age); //...other code };
Finally, we want to replace the hardcoded values in our JSX, with the values we receive from the props:
return ( <div className="contact-card"> <img src={props.avatar} alt="profile" /> <div className="user-details"> <p>Name: {props.name}</p> <p>Email: {props.email}</p> <button onClick={() => setShowAge(!showAge)}>Toggle Age </button> {showAge && <p>Age: {props.age}</p>} </div> </div> );
Notice how we have set the image source using whatever value we received from props. We did similar for name, email, and age. Also notice how we wrap this code in curly braces, so it gets executed as JavaScript.
Our final App.js file looks like this:
// App.js const App = () => { return ( <> <ContactCard avatar="https://via.placeholder.com/150" name="Jenny Han" email="[email protected]" age={25} /> <ContactCard avatar="https://via.placeholder.com/150" name="Jason Long" email="[email protected]" age={45} /> <ContactCard avatar="https://via.placeholder.com/150" name="Peter Pan" email="[email protected]" age={100} /> </> ); }; const ContactCard = props => { const [showAge, setShowAge] = useState(false); return ( <div className="contact-card"> <img src={props.avatar} alt="profile" /> <div className="user-details"> <p>Name: {props.name}</p> <p>Email: {props.email}</p> <button onClick={() => setShowAge(!showAge)}> Toggle Age </button> {showAge && <p>Age: {props.age}</p>} </div> </div> ); };
If you run this in the browser, you should see something similar to this:
Hurray! Our component works the same as before, but its now more dynamic. We can reuse the same ContactCard but passing in different data - whilst keeping the layout, styles, and state objects the same.
Rendering components from a List
Our contacts list is coming along nicely, we have some well crafted, reusable code so time to leave it alone right? Wrong! Let’s take it a step further.
In a real application, data usually comes in the form of an array of data, e.g after an API call. Let’s pretend we’ve made an API call to retrieve some users from a database and have received the following data:
const contacts = [ { name: "Jenny Han", email: "[email protected]", age: 25 }, { name: "Jason Long", email: "[email protected]", age: 45 }, { name: "Peter Pan", email: "[email protected]", age: 100 } ];
Paste this into the App() component at the top of the function. The eagled eye amongst you will notice how this data is similar to what we already have. But how we we turn this data into ContactCard components? Well, remember all those days you spent learning how to loop over an array using .map()? Now is the day we put that into action!
To display a list of components, we:
Loop over the array using .map()
For each item in the array, create a new ContactCard component
Pass the data from each object in the array to the ContactCard component as props
Let’s see how this works. In our appApp() component, replace the return statement with this:
return ( <> {contacts.map(contact => ( <ContactCard avatar="https://via.placeholder.com/150" name={contact.name} email={contact.email} age={contact.age} /> ))} </> );
As you can see, we map over the array. For each object in the array, we want to create a new ContactCard component. For the props, we want to take the name, email, and age from the current object the map function is on. In other words, from the contact variable.
NOTE: I’ve left the “avatar” prop alone, as this is the same for now - it’ll change later in the tutorial
And that’s it! Our App.js file looks like this:
//App.js const App = () => { const contacts = [ { name: "Jenny Han", email: "[email protected]", age: 25 }, { name: "Jason Long", email: "[email protected]", age: 45 }, { name: "Peter Pan", email: "[email protected]", age: 100 }, { name: "Amy McDonald", email: "[email protected]", age: 33 } ]; return ( <> {contacts.map(contact => ( <ContactCard avatar="https://via.placeholder.com/150" name={contact.name} email={contact.email} age={contact.age} /> ))} </> ); };
Run this in the browser and things should look the same. We haven’t change our ContactCard, merely changed where we got the data from. The cool thing about this is that if you added another row to the contacts array, the extra component will get rendered automatically- you don’t have to do anything else! Try this for yourself and see.
Pulling data from an API
We’ve got a nice looking React App now, nice and dynamic and things are working well. Which is a good place to be since we’re just getting started with React! But there are some tidy ups we need to make. In a real application, data will be pulled in from an API.
For the next part of the tutorial, we are going to get real contacts (when I say real contacts, I mean fake contacts - you know what I mean) from a real API: https://randomuser.me/. Feel free to browse the website and look at the response we will get back — this is where we will get our data to populate our components.
Firstly, let’s create a state variable to hold the data we get back from the API. Remember, state is good for holding that that can change. Our contacts list can definitely change!
In App.js, remove the contacts array add the following:
const [contacts, setContacts] = useState([]);
Here, we’re doing here is creating a state object, and initialising it to an empty Array. When we make the API call, we’ll update the state to contain a list of contacts. Since we named this state object contacts, our rendering logic within the JSX will look for this array instead (as opposed to the old contacts array we just deleted).
Next, let’s grab the data from the API. We’ll use the standard Fetch API. For now, we’ll log the data to the console. Add the following below the state object we just created:
fetch("https://randomuser.me/api/?results=3") .then(response => response.json()) .then(data => { console.log(data); });
All we’re doing here is:
Making a GET request to the randomuser API, asking for three results
Convert the response into JSON
Logging the JSON to the console.
If you run this in the browser, you’ll notice the ContactCard components no longer render - thats fine, we haven’t saved any new data to state yet, and our state variable is currently empty. If you look at the console (in your browser dev tools) you’ll notice the response object is logged. Which will look something like this:
You’ll see we have a results array, which has 3 objects. Each of these objects contain the details of a user (or a “Contact” in our case). This is similar to the contacts array we manually created ourselves in the previous section - just an array full of objects.
Let’s update our App components JSX to pick data from this object. Update the JSX like so:
return ( <> {contacts.map(contact => ( <ContactCard avatar={contact.picture.large} name={contact.name.first + " " + contact.name.last} email={contact.email} age={contact.dob.age} /> ))} </> );
This works similar to what we had before:
We are looping through the contacts variable (which, at the moment is an empty array)
When we eventually save the response to state (the next step) we look through each object in the array, for the appropriate things we need: in this case picture, name, email, and dob objects.
Next we want to store the results array in state, so our JSX can loop over it (using the map() function we seen previously) and render some lovely ContactCards. Within our fetch function, add the call to setContacts(data.results) like so:
fetch("https://randomuser.me/api/?results=3") .then(response => response.json()) .then(data => { console.log(data); setContacts(data.results); });
Our App component now looks like this:
//App.js const App = () => { const [contacts, setContacts] = useState([]); fetch("https://randomuser.me/api/?results=3") .then(response => response.json()) .then(data => { console.log(data); setContacts(data.results); }); return ( <> {contacts.map(contact => ( <ContactCard avatar={contact.picture.large} name={contact.name.first + " " + contact.name.last} email={contact.email} age={contact.dob.age} /> ))} </> ); };
If you save this, and run it in the browser, you’ll see something like this:
“WTF is going on everything is broken!”, don’t panic just yet (If you’re on a slower machine or just getting a bit freaked out, you can comment out the setContacts(data.results) line within the fetch function for now).
What’s happening here is that we’re stuck in a bit of a loop:
We make a call to fetch and get some data back
We then save this data to state
Remember, React does a re-render when the state changes
When the component re-renders, the fetch api call happens again, and sets the state
Since the state updated, the component re-renders again
After the component re-renders, fetch is called again…
You get the idea
So how do we stop this? We have to delete everything and start again. Nah just kidding, don’t run away yet. We can fix this with another built in React Hook - useEffect.
Introducing useEffect
The useEffect hook is a special hook that runs a function. By default, the useEffect hook runs on every re-render. However, we can configure it to only run under certain condition, e.g when a component mounts, or if a variable changes. The useEffect hook looks like this:
useEffect(() => { // code to run });
This will run every time. If we want to specify “only run once” we pass in an empty array as a second argument like so.
useEffect(() => { // code to run },[]); //<-- notice the empty array
This is called a dependency array. When the dependency array is empty, this means the useEffect function will only run when the component loads for the first time. For additional re-renders, the useEffect function is skipped.
This is a perfect place to put our API call, as we only want to get the data once, when the component loads. Go ahead and place a **useEffect()**function into our App component, and move the fetch API call into the useEffect function. Our App component now looks like this:
//App.js const App = () => { const [contacts, setContacts] = useState([]); useEffect(() => { fetch("https://randomuser.me/api/?results=3") .then(response => response.json()) .then(data => { setContacts(data.results); }); }, []); return ( <> {contacts.map(contact => ( <ContactCard avatar={contact.picture.large} name={contact.name.first + " " + contact.name.last} email={contact.email} age={contact.dob.age} /> ))} </> ); };
Now, if you run the code in your browser, you should see 3 contact cards appear! Refresh the page to see another randomised list of contacts:
Conclusion
Congrats! You just completed your first real-world app and laid the foundation to move onto more advanced topics.
Make sure to subscribe here to stay up to date with my latest React content, course discounts and early access, as well as some free stuff!
via freeCodeCamp.org https://ift.tt/2WAdIyb
0 notes
Link
There’s one aspect of JavaScript that always has me pulling my hair: closures. I work with React a lot, and the overlap there is that they can sometimes be the cause of stale props and state. We’ll get into exactly what that means, but the trouble is that the data we use to build our UI can be totally wrong in unexpected ways, which is, you know, bad.
Stale props and states
Long story short: it’s when code that is executed asynchronously has a reference to a prop or state that is no longer fresh, and thus, the value it returns is not the latest one. To be even more clear, let’s play around with the same stale reference example React has in its documentation.
function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }
(Live demo) Nothing fancy here. We have a functional component named Counter. It keeps track of how many times the user has clicked one button and shows an alert that displays how many times that button was clicked when clicking another button. Try this:
Click the “Click me” button. You are going to see the click counter go up.
Now click the “Show alert”button. Three seconds should go by and then trigger an alert telling you how many times you clicked the “Click me” button.
Now, click the “Show alert” button again and quickly click the “Click me” button before it triggers the alert in three seconds.
See what happens? The count shown on the page and the count shown in the alert do not match. The number in the alert is not just some random number, though. That number is the value the count variable had in the moment the asynchronous function inside the setTimeout was defined, which is the moment the “Show alert” button is clicked. That’s just how closures work. We’re not going to get into the specifics of them in this post, but here are some docs that cover them in greater detail. Let’s focus on how we can avoid these stale references with our states and props. React offers a tip on how to deal with stale dates and props in the same documentation where the example was pulled.
If you intentionally want to read the latest state from some asynchronous callback, you could keep it in a ref, mutate it, and read from it.
By keeping the value asynchronously in a ref, we can bypass stale references. If you need to know more about ref in functional components, React’s documentation has a lot more information. So, that begs the question: How can we keep our props or state in a ref? Let’s do it the dirty way first.
The dirty way to store props and state in a ref
We can easily create a ref using useRef() and use count as its initial value. Then, wherever the state is being updated, we set the ref.current property to the new value. Lastly, use ref.current instead of count in the asynchronous part of our code.
function Counter() { const [count, setCount] = useState(0); const ref = useRef(count); // Make a ref and give it the count function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + ref.current); // Use ref instead of count }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => { setCount(count + 1); ref.current = count + 1; // Update ref whenever the count changes }} > Click me </button> <button onClick={() => { handleAlertClick(); }} > Show alert </button> </div> ); }
(Live demo) Go ahead and do the same as last time. Click “Show alert” and then click “Click me” before the alert is triggered in three seconds. Now we have the latest value! Here’s why it works. When the asynchronous callback function is defined inside setTimeout, it saves a reference to the variables it uses, which is count in this case. This way, when the state updates, React not only changes the value but the variable reference in memory is completely different as well. This means that — even if the state’s value is non-primitive — the variable you are working with in your asynchronous callback is not the same in memory. An object that would typically keep its reference throughout different functions now has a different value. How does using a ref solve this? If we take a quick look at React’s docs again, we find an interesting, but easy-to-miss, bit of information:
[…] useRef will give you the same ref object on every render.
It doesn’t matter what we do. Throughout the lifetime of your component, React will give us the exact same ref object in memory. Any callback, no matter when it’s defined or executed, is working with the same object. No more stale reference.
The cleaner way to store props and state in a ref
Let’s be honest… using a ref like that is an ugly fix. What happens if your state is being updated in a thousand different places? Now you have to change your code and manually update the ref in all those places. That’s a no-no. We are going to make this more scalable by giving ref the value of the state automatically when the state changes. Let’s start by getting rid of the manual change to the ref in the “Click me”button. Next, we make a function called updateState that is called whenever we need to change the state. This function takes the new state as an argument and it sets the ref.current property to the new state and updates the state as well with that same value. Finally, let’s substitute the original setCount function React gives us with the new updateState function where the state is being updated.
function Counter() { const [count, setCount] = useState(0); const ref = useRef(count); // Keeps the state and ref equal function updateState(newState) { ref.current = newState; setCount(newState); } function handleAlertClick() { ... } return ( <div> <p>You clicked {count} times</p> <button onClick={() => { // Use the created function instead of the manual update updateState(count + 1); }} > Click me </button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }
(Live demo)
Using a custom hook
The cleaner solution works just fine. It gets the job done just like the dirty solution, but only calls a single function to update the state and ref. But guess what? We can do better. What if we need to add more states? What if we want to do this in other components too? Let’s take the state, ref and updateState function and make them truly portable. Custom hooks to the rescue! Outside the Counter component, we are going to define a new function. Let’s name it useAsyncReference. (It can be named anything, really, but note that it’s common practice to name custom hooks with “use” as a prefix.) Our new hook will have a single parameter for now. We’ll call it value. Our previous solution had the same information stored twice: once in the state and once in the ref. We are going to optimize that by keeping the value just in ref this time. In other words, we will create a ref and give it the value parameter as its initial value. Right after the ref, we will make an updateState function that takes the new state and sets it to the ref.current property. Lastly, we return an array with ref and the updateState function, very similar to what React does with useState.
function useAsyncReference(value) { const ref = useRef(value); function updateState(newState) { ref.current = newState; } return [ref, updateState]; } function Counter() { ... }
We are forgetting something! If we check the useRef documentation, we learn that updating a ref does not trigger a re-render. So, while ref has the updated value, we wouldn’t see the changes on screen. We need to force a re-render every time ref gets updated. What we need is a fake state. The value doesn’t matter. It’s only going to be there to provoke the re-render. We can even ignore the state and only keep its update function. We are calling that update function forceRender and giving it an initial value of false. Now, inside updateState, we force the re-render by calling forceRender and passing it a state different to the current one after setting ref.current to newState.
function useAsyncReference(value) { const ref = useRef(value); const [, forceRender] = useState(false); function updateState(newState) { ref.current = newState; forceRender(s => !s); } return [ref, updateState]; } function Counter() { ... }
Take whatever value it has and return the opposite. The state doesn’t really matter. We are merely changing it so React detects a change in state and re-renders the component. Next, we can clean the Count component and remove the previously used useState, ref and updateState function, then implement the new hook. The first value of the returned array is the state in the form of a ref. We’ll keep calling it count, where the second value is the function to update the state/ref. We’ll continue calling it setCount. We also have to change the references to the count since now that they all must be count.current. And we must call setCount instead of calling updateState.
function useAsyncReference(value) { ... } function Counter() { const [count, setCount] = useAsyncReference(0); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count.current); }, 3000); } return ( <div> <p>You clicked {count.current} times</p> <button onClick={() => { setCount(count.current + 1); }} > Click me </button> <button onClick={handleAlertClick}>Show alert</button> </div> ); }
Making this work with props
We have a truly portable solution for our problem. But guess what… there’s still a little more to do. Specifically, we need to make the solution compatible with props. Let’s take the “Show alert” button and handleAlertClick function to a new component outside the Counter component. We are gonna call it Alert and it’s going to take a single prop called count. This new component is going to show the count prop value we are passing it in an alert after a three second delay.
function useAsyncReference(value) { ... } function Alert({ count }) { function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + count); }, 3000); } return <button onClick={handleAlertClick}>Show alert</button>; } function Counter() { ... }
In Counter, we’re swapping the “Show alert” button for the Alert component. We’ll pass count.current to the count prop.
function useAsyncReference(value) { ... } function Alert({ count }) { ... } function Counter() { const [count, setCount] = useAsyncReference(0); return ( <div> <p>You clicked {count.current} times</p> <button onClick={() => { setCount(count.current + 1); }} > Click me </button> <Alert count={count.current} /> </div> ); }
(Live demo) Alright, time to run through the testing steps again. See? Even though we are using a safe reference to the count in Counter, the reference to the count prop in the Alert component is not asynchronously safe and our custom hook is not suitable to use with props… yet. Lucky for us, the solution is fairly simple. All we have to do is add a second parameter to our useAsyncReference hook named isProp, with false as the initial value. Just before we return the array with ref and updateState, we set up a condition. If isProp is true, we set the ref.current property to value and only return ref.
function useAsyncReference(value, isProp = false) { const ref = useRef(value); const [, forceRender] = useState(false); function updateState(newState) { ref.current = newState; forceRender(s => !s); } if (isProp) { ref.current = value; return ref; } return [ref, updateState]; } function Alert({ count }) { ... } function Counter() { ... }
Now let’s update Alert so that is uses the hook. Remember to pass true as a second argument to useAsyncReference since we are passing a prop and not a state.
function useAsyncReference(value) { ... } function Alert({ count }) { const asyncCount = useAsyncReference(count, true); function handleAlertClick() { setTimeout(() => { alert("You clicked on: " + asyncCount.current); }, 3000); } return <button onClick={handleAlertClick}>Show alert</button>; } function Counter() { ... }
(Live demo) Give it another try. Now it works perfectly whether you use states or props.
One last thing…
There’s one last change I’d like to make. React’s useState docs tell us that React will bail out of a re-render if the new state is identical to the previous one. Our solution doesn’t do that. If we pass the current state again to the hook’s updateState function, we will force a re-render no matter what. Let’s change that. Let’s put the body of updateState inside an if statement and execute it when ref.current is different than the new state. The comparison must be done with Object.is(), just like React does.
function useAsyncReference(value, isProp = false) { const ref = useRef(value); const [, forceRender] = useState(false); function updateState(newState) { if (!Object.is(ref.current, newState)) { ref.current = newState; forceRender(s => !s); } } if (isProp) { ref.current = value; return ref; } return [ref, updateState]; } function Alert({ count }) { ... } function Counter() { ... }
Now we are finally done!
React can sometimes seem like a black box that is full of little quirks. Those quirks might be daunting to deal with, like the one we just tackled. But if you are patient and enjoy being challenged, you’ll soon realize it’s an awesome framework and a pleasure to work with.
0 notes