#react onclick call function with parameters
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
React on Event Types
React is a popular JavaScript library used for building user interfaces. One of its key features is the ability to handle events efficiently and effectively. In this article, we will explore different event types in React and how to handle them.
1. Introduction to React Events
In React, events are actions or occurrences that happen in the browser, such as a button click, input change, or form submission. React provides a synthetic event system that normalizes event handling across different browsers and provides additional functionality for managing UI updates and state.
2. Event Handling in React
Event handling in React can be done in two main ways: using class components and functional components. 2.1 Event Handling in Class Components In class components, event handling involves creating event handler methods. For example, to handle a button click event, you can define an onClick event handler method: class MyComponent extends React.Component { handleClick() { // Event handler logic } render() { return ( Click ); } } 2.2 Event Handling in Functional Components In functional components, event handling can be achieved using the useState hook along with event handler functions. For instance, to handle an input field change event, you can define an onChange event handler function: import React, { useState } from 'react'; function MyComponent() { const = useState(''); function handleChange(event) { setValue(event.target.value); } return ( ); }
3. Common Event Types
React provides support for various event types. Here are some of the commonly used event types: 3.1 onClick Event The onClick event occurs when an element is clicked, typically used with buttons or links. When a user clicks the element, the event handler is invoked, and the specified logic is executed. 3.2 onChange Event The onChange event occurs when the value of an input field changes, commonly used for handling text input. Whenever the input value changes, the event handler is called, and the UI can be updated based on the input value. 3.3 onSubmit Event The onSubmit event occurs when a form is submitted, usually used for form submission handling. When a user submits the form, the event handler is triggered, and the form data can be processed. 3.4 onMouseOver Event The onMouseOver event occurs when the mouse pointer is moved over an element, often used for tooltips or dropdown menus. When a user hovers over the element, the event handler is invoked, and the associated actions related to that element can be performed.
4. Writing Event Handler Functions
In React, event handler functions are typically written within the component where the event occurs. The event handler function is responsible for updating the state or executing other logic based on the user's action. class MyComponent extends React.Component { handleClick() { // Event handler logic } render() { return ( Click ); } } import React, { useState } from 'react'; function MyComponent() { const = useState(''); function handleChange(event) { setValue(event.target .value); } return ( ); }
5. Event Propagation and Default Behavior
React provides mechanisms to control event propagation and default behavior. Event propagation refers to the process of an event being passed to its parent components, while default behavior refers to the browser's default actions. To control event propagation, you can use the stopPropagation() method, which prevents the event from being propagated to the parent components. To prevent the default behavior, you can use the preventDefault() method, which stops the browser from performing its default action.
6. Event Delegation
Event delegation in React refers to handling events on parent elements instead of individual child elements. This approach simplifies event handling for dynamically generated elements. By using event delegation, there is no need to attach event handlers to each individual child element.
7. Passing Parameters to Event Handlers
There are scenarios where you need to pass parameters to event handlers in React. For example, if you want to obtain information about a specific item when clicking on a list item. In such cases, you can pass additional parameters to the event handler.
8. Event Handling with External Libraries in React
React provides integration support for various external libraries. When using external libraries, you need to follow their event handling approaches. Integrating events between React components and external libraries requires careful consideration and may require additional configuration or measures, if necessary.
9. Event Optimization in React
In React, event optimization can be crucial for performance improvement. Event optimization aims to prevent unnecessary re-renders and enhance the responsiveness of your application. Techniques such as function binding, memoization, and maintaining immutability can be applied for event optimization.
10. Conclusion
In this article, we explored event handling in React. React offers a wide range of event types that enable you to handle user interactions and implement dynamic UIs. We learned how to write event handler functions, control event propagation and default behavior, use event delegation, pass parameters to event handlers, integrate events with external libraries, and optimize events in React. Read the full article
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
React Hooks are a simpler way to encapsulate stateful behavior and side effects in functional components instead of classes. Some hooks are easier to understand than others, therefore this series of posts will focus on demystifying the hooks that are not as straightforward.
So far, we have explored useCallback, useMemo, useRef, and useContext so make sure to checkout my previous posts if you haven’t already. This week, let’s start with the basics by explaining the JavaScript reduce method. Once we explore the basics, it will be much easier to understand the useReducer hook, as well as how and when to use it in your code.
What is a reducer?
A reducer is the action that will be executed in order to get only one value. The goal of a reducer is to reduce (...duh!). The value returned could be a number, a string, an array or even an object, as long as it is a singular value. Moreover, it is important to highlight that reducers return a new value instead of mutating your initial value.
reduces are very useful when you want to obtain a single value after applying some logic to a group of values. For instance, if you want to add up an array of numbers to obtain a total value as we do in the following example.
We apply the JavaScript reduce method to an array of numbers called nums = [1,2,3,4,5]. The reduce method takes two parameters:
reducer - a function that provides instructions to obtain one value. In this case, to sum up all the given values in the nums array.
const reducer = (accumulator, currentValue) => accumulator + currentValue;
initialValue - the starting point value when implementing the reducer function's instructions. In our example, we define our initial value as 0 so the total value returned reflects only the sum of the values in the array nums.
const initialValue = 0;
Now that we defined the elements involved, let’s see it all together. The reducemethod takes our initialValue and builds on it by following the instructions given under the reducer function, adding each value in the nums array until it is able to return one total value.
// reducer method - the action that will be executed in order to get one value const reducer = (accumulator, currentValue) => accumulator + currentValue; // array of values that we want to add up const nums = [1,2,3,4,5]; // initial value set to 0 const initialValue = 0; // JavaScript reduce method receives two parameters: the reducer function and initial value defined above const totalValue = nums.reduce(reducer, initialValue);
What is useReducer()?
The useReducer hook is used with state management. It receives the following parameters:
reducer - a function that provides instructions on how to manage state. It takes two parameters state and action and it returns a new state.
// reducer type (state, action) => newState
initialState - the starting point value. It will change according to the reducerinstructions.
Does it look familiar? Well yeah... It takes similar parameters as the reducefunction explained above. However, the useReducer hook does not return just one value as reduce does. Instead it returns two elements as an array, the current state and a dispatch function.
const [state, dispatch] = useReducer(reducer, initialState);
If you are familiar with React hooks, you probably have used useState before. Let’s compare these two hooks
// useState implementation const [state, setState] = useState(initialState); // useReducer implementation const [state, dispatch] = useReducer(reducer, initialState);
useReducer and useState both return a stateful value (state), and a function to update the state (setState and dispatch). In addition, both hooks receive an initial state value (initialValue). The main difference in these two initializations is that useReducer also takes a reducer function, which will be called when we use the returned dispatch function. Let’s explore how useReducer works in the next section.
How to use useReducer?
Sometimes the best way to explain how something works is with an example so let’s take a look at one. Here is a definition for an initialState, also called a store of data, that contains a list of dogs up for adoption with their name, breed, and adoption status.
const initialState = [ { name: "Waffles", breed: "Chihuahua", adopted: false, }, { name: "Charlie", breed: "Pitbull", adopted: true, }, { name: "Prince", breed: "German Shepherd", adopted: false, }, ];
Now let’s create a reducer function to update our initialState list of dogs as they get adopted or returned. This function takes the following parameters:
state - the current state of our dogs in adoption.
action - an object that contains the following:
type of action we want to perform. In this case, we are only building two options, adopt or ‘return’.
payload optional data. In our example, we will pass the dog’s name so we can identify which dog got adopted or returned.
const reducer = (state, action) => { switch (action.type) { case 'ADOPT': return state.map(dog => { if (dog.name === action.payload) dog.adopted = true; return dog; }); case 'RETURN': return state.map(dog => { if (dog.name === action.payload) dog.adopted = false; return dog; }); default: return state; } }
Now It’s finally time to implement our useReducer() hook! Take a look at the example below, where we define our useReducer() hook with the initialState(adoption dogs list) and the reducer function we created to handle their adoption status.
We iterate through our dogs list state and display a button that will say adopt or return depending on their current adoption status. The onClick handler assigned to our button will call a function in charge of using the dispatch function returned by our useReducer() hook passing the type of action it needs to perform and the dog’s name as payload.
const adoptDog = name => dispatch({ type: 'ADOPT', payload: name });
The dispatch function will pass this data to our reducer function, where we will use the type to identify what section of code needs to run and the payload to find the dog record we need to update.
When should you use useReducer?
When explaining the useReducer hook, we compared it to useState so you might be wondering… when should I use useReducer and when should I use useState?
The useReducer hook is a preferred alternative to useState when dealing with the following:
Complex state logic that involves multiple sub-values
State values that depend on the state of other state elements
Summary
The JavaScript reduce method is very useful when you want to obtain a single value after applying some logic to a group of values.
reducers return a new value instead of mutating your initial value.
The useReducer hook is used with state management.
The useReducer hook should be used when dealing with complex state logic, multiple sub-values, or when your state depends on state sub-values.
I hope this post helped you get a better understanding of the JavaScript reducemethod and the useReducer() hook and that you will start taking advantage of these concepts in your future projects.
I enjoy creating content that explains concepts in really simple terms. Why?Because knowledge is power and I want to help beginner developers expand their knowledge and thrive.
0 notes
Text
React: The Basics
React JS is today's most popular JavaScript library for building User Interfaces, which has created by Facebook. We can build modern, fast Single Page Applications or websites with React. React is so popular in the market and beneficial to know for a Web/Frontend Developer.
Is React JS a Library or a Framework?
This is one of the most unclear subjects of React. Let’s make this clear from the beginning. React is a Library, not a Framework.
What is a Library?
A library in programming can be explained as a collection of codes. We use a library to write code in a much simpler way or to import a feature from it into our project. JQuery is a library for example. We can write JavaScript much simpler by using JQuery, or we can import written JQuery features to our project. The project itself is not dependent on a library.
What is a Framework?
A Framework, on the other hand, is a complete package of code with its own functionalities & libraries. A Framework has its own rules, you don’t have much flexibility and the project is dependent on the Framework you use. Angular is an example of a framework. So React is for building User Interfaces, and how you program the rest of the project is up to you. Like JQuery, you can include React in your project partially, or completely. So React JS a library.
React Virtual DOM
To understand the importance of React Virtual DOM, first, you need to know what DOM (Document Object Model) is. DOM is basically a representation of the HTML code on a webpage. The document is the web page itself, the objects are the HTML tags. And finally, the model of DOM is a tree structure:
The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page so that programs can change the document structure, style, and content. The DOM represents the document as nodes and objects. That way, programming languages can connect to the page.
A Web page is a document. This document can be either displayed in the browser window or as the HTML source. But it is the same document in both cases. The Document Object Model (DOM) represents that same document so it can be manipulated. The DOM is an object-oriented representation of the web page, which can be modified with a scripting language such as JavaScript.
What is the benefit of Virtual DOM?
Each time you make a change in the code, DOM will be completely updated and rewritten. This is an expensive operation and consumes lots of time. In this point, React provides a solution: The Virtual DOM.
So when something changes:
React first creates an exact copy of the DOM
Then React figures out which part is new and only updates that specific part in the Virtual DOM
Finally, React copies only the new parts of the Virtual DOM to the actual DOM, rather than completely rewriting it.
This approach makes a webpage much faster than a standard webpage. That’s also one of the reasons why React is so popular.
So what is this JSX?
JSX (JavaScript XML) is a syntax extension to JavaScript used by React. JSX is basically used to write HTML tags inside JavaScript. Later, the JSX code will be translated into normal JavaScript, by Babel.
In summary, React doesn’t have HTML files, HTML tags are rendered directly inside JavaScript. This approach makes React faster.
What is a React Component?
A component is an independent, reusable code block, which divides the UI into smaller pieces. In other words, we can think of components as LEGO blocks. Likewise, we create a LEGO structure from many little LEGO blocks, we create a webpage or UI from many little code blocks (components).
We don’t really want to have thousands of lines of code together in one single file. Maintenance of the code gets more and more complex as the project gets bigger. In this point, dividing the source code into components helps us a lot. Each component has its own JS and CSS code, they are reusable, easier to read, write and test. In web development, as the reasons I explained above, it’s beneficial to use component-based technologies, and React JS is one of them.
React has 2 types of components: Functional (Stateless) and Class (Stateful).
Functional (Stateless) Components
A functional component is basically a JavaScript (or ES6) function which returns a React element. According to React official docs, the function below is a valid React component:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
IMPORTANT: Functional components are also known as stateless components
So a React Functional Component:
Is a JavaScript / ES6 function
Must return a React element
Take props as a parameter if necessary
Class (Stateful) Components
Class components are ES6 classes. They are more complex than functional components including constructors, life-cycle methods, render( ) function and state (data) management. In the example below, we can see how a simple class component looks like:
import React, { Component } from 'react';
class ParentComponent extends Component {
render() {
return <h1>I'm the parent component.</h1>;
}
}
export default ParentComponent;
So, a React class component:
It is an ES6 class, will be a component once it ‘extends’ React component.
Can accept props (in the constructor) if needed
Can maintain its own data with state
Must have a render( ) method which returns a React element (JSX) or null
Props
Let’s start by defining Component’s props (obviously short for properties) in React. Props are used to customize Component when it’s being created and give it different parameters.
import React, {Component} from 'react'
class Topic extends Component {
render{
return(
<div>
{this.props.name}
</div>
)
}
}
One of the most important features of props is that they can be passed by a parent component to its child components. This allows us to create a component that can be customized with a new set of props every time we use it.
import React, {Component} from 'react'
class Welcome extends Component {
render{
return(
<div>
<p> Welcome to React, today you will learn: </p>
<Topic name="Props"/>
<Topic name="State"/>
</div>
)
}}
Props are passed to the component and are fixed throughout its lifecycle. But there are cases when we want to use data that we know is going to change over time. In this case, we use something called state.
State
Unlike props, the state is a private feature and it strictly belongs to a single Component. The state allows React components to dynamically change output over time in response to certain events.
Component’s state is initialized inside a constructor:
class Counter extends Component{
constructor(props){
super(props);
this.state = {counter: 0}
}
render(){
return(
<p>{this.state.counter}</p>
)
}
And can be changed later using inbuilt setState() function
class Counter extends Component{
constructor(props){
super(props);
this.state = {counter: 0}
this.increment = this.increment.bind(this);
}
increment(){
this.setState({counter: this.state.counter + 1})
}
render(){
return(
<button onClick={this.increment}>Like</button>
<p>{this.state.counter}</p>
)
}
Lifecycle of Components
Each component in React has a lifecycle that you can monitor and manipulate during its three main phases.
The three phases are Mounting, Updating, and Unmounting.
Common React Lifecycle Methods
render()
componentDidMount()
componentDidUpdate()
componentWillUnmount()
render()
The render() method is the most used lifecycle method. You will see it in all React classes. This is because render() is the only required method within a class component in React. As the name suggests it handles the rendering of your component to the UI. It happens during the mounting and updating of your component.
componentDidMount()
Now your component has been mounted and ready, that’s when the next React lifecycle method componentDidMount() comes in play. componentDidMount() is called as soon as the component is mounted and ready. This is a good place to initiate API calls if you need to load data from a remote endpoint.
componentDidUpdate()
This lifecycle method is invoked as soon as the updating happens. The most common use case for the componentDidUpdate() method is updating the DOM in response to prop or state changes. You can call setState() in this lifecycle, but keep in mind that you will need to wrap it in a condition to check for state or prop changes from the previous state. Incorrect usage of setState() can lead to an infinite loop.
componentWillUnmount()
As the name suggests this lifecycle method is called just before the component is unmounted and destroyed. If there are any cleanup actions that you would need to do, this would be the right spot.
Routing
Routing is a key aspect of web applications (and even other platforms) that could not be left out in React. We can make full-fleshed single-page applications with React if we harness the powers of routing. This does not have to be a manual process, we can make use of React-Router.
Switch
Switch component helps us to render the components only when path matches otherwise it fallbacks to the not found component.
<Switch>
<Route exact path="/" component={App} />
<Route path="/users" component={Users} />
<Route path="/contact" component={Contact} />
<Route component={Notfound} />
</Switch>
Browser Router
A <Router> that uses the HTML5 history API (pushState, replaceState, and the popstate event) to keep your UI in sync with the URL.
<BrowserRouter
basename={optionalString}
forceRefresh={optionalBool}
getUserConfirmation={optionalFunc}
keyLength={optionalNumber}>
<App />
</BrowserRouter>
Go through this link for better understanding of Routes: Getting started with React Router
Handling Events
Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences:
React events are named using camelCase, rather than lowercase.
With JSX you pass a function as the event handler, rather than a string
<button onClick={activateLasers}></button>
Named Export vs Default Export in ES6
Named Export: (export)
With named exports, one can have multiple named exports per file. Then import the specific exports they want to be surrounded in braces. The name of the imported module has to be the same as the name of the exported module.
// imports
// ex. importing a single named export
import { MyComponent } from "./MyComponent";
// ex. importing multiple named exports
import { MyComponent, MyComponent2 } from "./MyComponent";
// ex. giving a named import a different name by using "as":
import { MyComponent2 as MyNewComponent } from "./MyComponent";
// exports from ./MyComponent.js file
export const MyComponent = () => {}
export const MyComponent2 = () => {}
Default Export: (export default)
One can have only one default export per file. When we import we have to specify a name and import like:
// import
import MyDefaultComponent from "./MyDefaultExport";
// export
const MyComponent = () => {}
export default MyComponent;
Getting Started:
You can just run the create-react-app on the command line, followed by the name of the app you want to create. This creates the react app, with all the necessary functionality you need, already built into the app. Then you can just cd into the react app and start it with npm start.
Command: create-react-app my-app
Basic Structure
node_modules is where packages installed by NPM or Yarn will reside.
src is where your dynamic files reside. If the file is imported by your JavaScript application or changes contents, put it here.
public is where your static files reside.
0 notes
Text
Managing State in React using Unstated-Next
In a previous post, we saw how to manage state using Unstated. As you might recall, Unstated uses React’s built-in setState to allow you create components that can consume state by subscribing to a provider — like the React’s Context API.
Well, we’re going to build off that last post by looking at Unstated Next, a library that author Jamie Kyle identifies as the “spiritual successor" to his Unstated project. Unstated Next provides both React Hooks and the Context API for managing state. Unstated was a minimal abstraction to the idea of React Hooks before they were a fully-fledged thing. But now that Hooks in React are so good, that abstraction is unnecessary and Unstated Next simply incorporates them while providing an API to share state and logic with the Hooks.
We’re going to specifically look at how to manage state in both single and multiple components using Unstated Next. It might be helpful to check out the previous post on Unstated prior to moving ahead, but it’s not totally necessary.
Example: A minimal form component
To start off, we’ll create a tiny React application for a form that merely contains a text input for a person’s name and a button to submit it. When the button is clicked, we’ll display the name as a paragraph element above the form. The source code for this example is available on GitHub.
This is going to be a Bootstrap React application which we can spin up by using Create React App. Let’s install that and then change directories into the project folder.
npx create-react-app unstated-next-form cd unstated-next-form>
We need to add Unstated Next as a dependency:
## yarn yarn add unstated-next ## npm npm install --save unstated-next
We’ll be making use of React Hooks and createContainer from Unstated Next, so let’s import those into the App component:
// src/App.js import React, { useState } from 'react'; import { createContainer } from "unstated-next";
Next, we will create a custom hook. We’ll have our state in it, which we can create using useState:
// src/App.js // ...same as before const useForm = () => { const [input, setValue] = useState(""); const [name, setName] = useState("Barney Stinson"); const handleInput = event => { setValue(event.target.value); }; const updateName = event => { event.preventDefault(); setName(input); setValue(""); }; return { input, name, handleInput, updateName, }; };
We have two states defined here. input will be used for keeping track of values entered into the text input and it will be updated using the handleInput method. name will be updated when the button is clicked, which will trigger the updateName method.
OK, now we can create a container by passing our custom hook as a parameter to the createContainer() method.
// src/App.js // ...same as before const FormContainer = createContainer(useForm);
This will create a container which we can use across our application. Yeah, you read that right, but let’s take one step at a time. We’re starting with this one component to see how it works with Unstated Next.
Now, let’s create a Form component that looks like this.
// src/App.js // ...same as before const Form = () => { const form = FormContainer.useContainer(); return ( <div> <p>Hello! {form.name}</p> <div> <input type="text" value={form.input} onChange={form.handleInput} /> <button onClick={form.updateName}>Save</button> </div> </div> ); };
We’re assigning the variable form to the value obtained from calling FormContainer.useContainer(). The value contains the states and methods defined in the custom hook we created above. With that, we can make use of the state and methods provided — but for that to happen, we have to wrap the Form component in a provider.
const App = () => ( <Form.Provider> <Form /> </Form.Provider> )
With what you have learned so far, try building a minimal to-do application using Unstated Next. If you get stuck, feel free to check this repository to see how I made mine.
Example: Sharing state across multiple components
OK, so you got a hint earlier that we can use our form container anywhere we’d like. One of the benefits of using Unstated Next is that it makes it possible to share state across multiple components. To see how this works, we’ll build a tiny app that uses the form features we made above and also makes it possible to create to-do tasks using the same state. The name of the user can be updated in the form component, and this update will also be reflected in the to-do component. Two birds of a feather!
There’s a repo for this example as well, so feel free to clone or download it as we plow ahead.
Let’s spin up a new project and install the necessary dependencies:
npx create-react-app unstated-next-app cd unstated-next-app yarn unstated-next shortid
The state for the application will live in a separate file. We want to have the states for the form and to-do components in the store, and the methods needed for updating them too. Create a store.js file inside the src directory and make it look like this;
// src/store.js import { useState } from "react"; import shortid from "shortid" import { createContainer } from 'unstated-next' export const useStore = () => { // Construct a list that contains two default tasks const list = [ { id: 1, title: 'Write code' }, { id: 2, title: 'Buy milk' } ] const [input, setValue] = useState(""); // Let's set a legen -- wait for it -- dary default name that updates on form submit const [name, setName] = useState("Barney Stinson"); const [todos, addTodo] = useState(list); const [item, setTodo] = useState(""); const handleInput = event => { setValue(event.target.value); }; const updateName = event => { event.preventDefault(); setName(input); setValue(""); }; const handleTodo = event => { setTodo(event.target.value); }; const handleSubmit = event => { event.preventDefault(); const value = { id: shortid.generate(), title: item } addTodo(todos.concat(value)); setTodo(""); }; return { input, name, handleInput, updateName, todos, item, handleTodo, handleSubmit }; } export const StoreContainer = createContainer(useStore)
We make use of useState() to create the states we need. The methods are defined and all of this happens inside the custom hook, useStore(). We create the StoreContainer and then pass useStore() as a parameter to createContainer(). With that, we can make use of the StoreContainer in the necessary components where want to make use of the state and methods we have defined.
Starting with the form section, create a file called form.js and it should look like what I have below;
// src/form.js import React from "react"; import { StoreContainer} from "./store"; const FormComponent = () => { const form = StoreContainer.useContainer(); return ( <div> <p>Hello! {form.name}</p> <div> <input type="text" value={form.input} onChange={form.handleInput} /> <button onClick={form.updateName}>Change Name</button> </div> </div> ); }; export default FormComponent;
We’re using StoreContainer to access the state and methods we need. We’ll do the same thing for the task component which you can create in a todo.js file.
// src/todo.js import React from "react"; import { StoreContainer } from "./store"; const TodoComponent = () => { const todo = StoreContainer.useContainer(); return ( <div> <p>Add Todos</p> <input type="text" value={todo.item} onChange={todo.handleTodo} /> <button onClick={todo.handleSubmit}>Add</button> <div> <p>Dear {todo.name}, here are your current tasks;</p> {todo.todos.map((item) => { return ( <ul key={item.id}> <li>{item.title}</li> </ul> ); })} </div> </div> ); }; export default TodoComponent;
You can see that todo.name can only be updated in the FormComponent. That’s because we need a way to provide the state in both components. That’s why we’re going to turn again to Provider and add one in the App component just like we did in the previous example.
import React from 'react'; import TodoContainer from "./todo"; import FormContainer from "./form"; import { StoreContainer } from "./store" function App() { return ( <div className="App"> <StoreContainer.Provider> <FormContainer /> <TodoContainer /> </StoreContainer.Provider> </div> ); } export default App;
There we go! By adding the provider, data can be taken from the form component, stored in the provider, and passed back to the task list. 💥
The post Managing State in React using Unstated-Next appeared first on CSS-Tricks.
Managing State in React using Unstated-Next published first on https://deskbysnafu.tumblr.com/
0 notes
Text
Managing State in React using Unstated-Next
In a previous post, we saw how to manage state using Unstated. As you might recall, Unstated uses React’s built-in setState to allow you create components that can consume state by subscribing to a provider — like the React’s Context API.
Well, we’re going to build off that last post by looking at Unstated Next, a library that author Jamie Kyle identifies as the “spiritual successor" to his Unstated project. Unstated Next provides both React Hooks and the Context API for managing state. Unstated was a minimal abstraction to the idea of React Hooks before they were a fully-fledged thing. But now that Hooks in React are so good, that abstraction is unnecessary and Unstated Next simply incorporates them while providing an API to share state and logic with the Hooks.
We’re going to specifically look at how to manage state in both single and multiple components using Unstated Next. It might be helpful to check out the previous post on Unstated prior to moving ahead, but it’s not totally necessary.
Example: A minimal form component
To start off, we’ll create a tiny React application for a form that merely contains a text input for a person’s name and a button to submit it. When the button is clicked, we’ll display the name as a paragraph element above the form. The source code for this example is available on GitHub.
This is going to be a Bootstrap React application which we can spin up by using Create React App. Let’s install that and then change directories into the project folder.
npx create-react-app unstated-next-form cd unstated-next-form>
We need to add Unstated Next as a dependency:
## yarn yarn add unstated-next ## npm npm install --save unstated-next
We’ll be making use of React Hooks and createContainer from Unstated Next, so let’s import those into the App component:
// src/App.js import React, { useState } from 'react'; import { createContainer } from "unstated-next";
Next, we will create a custom hook. We’ll have our state in it, which we can create using useState:
// src/App.js // ...same as before const useForm = () => { const [input, setValue] = useState(""); const [name, setName] = useState("Barney Stinson"); const handleInput = event => { setValue(event.target.value); }; const updateName = event => { event.preventDefault(); setName(input); setValue(""); }; return { input, name, handleInput, updateName, }; };
We have two states defined here. input will be used for keeping track of values entered into the text input and it will be updated using the handleInput method. name will be updated when the button is clicked, which will trigger the updateName method.
OK, now we can create a container by passing our custom hook as a parameter to the createContainer() method.
// src/App.js // ...same as before const FormContainer = createContainer(useForm);
This will create a container which we can use across our application. Yeah, you read that right, but let’s take one step at a time. We’re starting with this one component to see how it works with Unstated Next.
Now, let’s create a Form component that looks like this.
// src/App.js // ...same as before const Form = () => { const form = FormContainer.useContainer(); return ( <div> <p>Hello! {form.name}</p> <div> <input type="text" value={form.input} onChange={form.handleInput} /> <button onClick={form.updateName}>Save</button> </div> </div> ); };
We’re assigning the variable form to the value obtained from calling FormContainer.useContainer(). The value contains the states and methods defined in the custom hook we created above. With that, we can make use of the state and methods provided — but for that to happen, we have to wrap the Form component in a provider.
const App = () => ( <Form.Provider> <Form /> </Form.Provider> )
With what you have learned so far, try building a minimal to-do application using Unstated Next. If you get stuck, feel free to check this repository to see how I made mine.
Example: Sharing state across multiple components
OK, so you got a hint earlier that we can use our form container anywhere we’d like. One of the benefits of using Unstated Next is that it makes it possible to share state across multiple components. To see how this works, we’ll build a tiny app that uses the form features we made above and also makes it possible to create to-do tasks using the same state. The name of the user can be updated in the form component, and this update will also be reflected in the to-do component. Two birds of a feather!
There’s a repo for this example as well, so feel free to clone or download it as we plow ahead.
Let’s spin up a new project and install the necessary dependencies:
npx create-react-app unstated-next-app cd unstated-next-app yarn unstated-next shortid
The state for the application will live in a separate file. We want to have the states for the form and to-do components in the store, and the methods needed for updating them too. Create a store.js file inside the src directory and make it look like this;
// src/store.js import { useState } from "react"; import shortid from "shortid" import { createContainer } from 'unstated-next' export const useStore = () => { // Construct a list that contains two default tasks const list = [ { id: 1, title: 'Write code' }, { id: 2, title: 'Buy milk' } ] const [input, setValue] = useState(""); // Let's set a legen -- wait for it -- dary default name that updates on form submit const [name, setName] = useState("Barney Stinson"); const [todos, addTodo] = useState(list); const [item, setTodo] = useState(""); const handleInput = event => { setValue(event.target.value); }; const updateName = event => { event.preventDefault(); setName(input); setValue(""); }; const handleTodo = event => { setTodo(event.target.value); }; const handleSubmit = event => { event.preventDefault(); const value = { id: shortid.generate(), title: item } addTodo(todos.concat(value)); setTodo(""); }; return { input, name, handleInput, updateName, todos, item, handleTodo, handleSubmit }; } export const StoreContainer = createContainer(useStore)
We make use of useState() to create the states we need. The methods are defined and all of this happens inside the custom hook, useStore(). We create the StoreContainer and then pass useStore() as a parameter to createContainer(). With that, we can make use of the StoreContainer in the necessary components where want to make use of the state and methods we have defined.
Starting with the form section, create a file called form.js and it should look like what I have below;
// src/form.js import React from "react"; import { StoreContainer} from "./store"; const FormComponent = () => { const form = StoreContainer.useContainer(); return ( <div> <p>Hello! {form.name}</p> <div> <input type="text" value={form.input} onChange={form.handleInput} /> <button onClick={form.updateName}>Change Name</button> </div> </div> ); }; export default FormComponent;
We’re using StoreContainer to access the state and methods we need. We’ll do the same thing for the task component which you can create in a todo.js file.
// src/todo.js import React from "react"; import { StoreContainer } from "./store"; const TodoComponent = () => { const todo = StoreContainer.useContainer(); return ( <div> <p>Add Todos</p> <input type="text" value={todo.item} onChange={todo.handleTodo} /> <button onClick={todo.handleSubmit}>Add</button> <div> <p>Dear {todo.name}, here are your current tasks;</p> {todo.todos.map((item) => { return ( <ul key={item.id}> <li>{item.title}</li> </ul> ); })} </div> </div> ); }; export default TodoComponent;
You can see that todo.name can only be updated in the FormComponent. That’s because we need a way to provide the state in both components. That’s why we’re going to turn again to Provider and add one in the App component just like we did in the previous example.
import React from 'react'; import TodoContainer from "./todo"; import FormContainer from "./form"; import { StoreContainer } from "./store" function App() { return ( <div className="App"> <StoreContainer.Provider> <FormContainer /> <TodoContainer /> </StoreContainer.Provider> </div> ); } export default App;
There we go! By adding the provider, data can be taken from the form component, stored in the provider, and passed back to the task list. 💥
The post Managing State in React using Unstated-Next appeared first on CSS-Tricks.
😉SiliconWebX | 🌐CSS-Tricks
0 notes
Text
SVG Web Page Components For IoT And Makers (Part 2)
SVG Web Page Components For IoT And Makers (Part 2)
Richard Leddy
2019-05-15T13:30:16+02:002019-05-15T15:35:00+00:00
So, we already have ways of dynamically loading a menu of SVG icons made to react by loading panels if we so desire, but the icons were not actual components. We were able to use a simple trick of bringing in the SVG for each icon and passing it into the Vue application. It was simple enough to generate a list of icons, and each icon reacted in a similar way except for small data differences. The data difference made it possible to bind the name of a panel to each icon in such a way that the handler for the icon’s button click could pass it on.
When a panel is loaded in the form of Vue component, everything about the panel and its components has to be loaded, templates, JavaScript, and more. So, the job of just managing loading the panel is bigger than what we have encountered so far in this discussion.
Let’s look at Vue’s way of providing a hook for async loading. The following snippet is from the Vue guide.
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // Pass the component definition to the resolve callback resolve({ template: '<div>I am async!</div>' }) }, 1000) })
The guide tells us that the setTimeout function is an example of how to use the synchronicity with Vue components. Notice that where before there had been an object as the second parameter to Vue.component, there is now a function, which is referred to as a factory function. Within the resolve callback is a component definition, that would have been the second parameter to Vue.component before.
So, I had to stare at this example a while before it made sense to me. Here is another example, which suits me better:
Vue.component('async-example', function (resolve, reject) { // Vue will call this function and promise itself to handle // it when it gets back with data. // this function can then call a promising object loader // here the 'loader' function is some abstract function. // Most likely the application will use 'fetch' // but it could be something else. loader('/my/resource/on/server.json'). then(function (JSON_data) { var object = transformJSONToJSObject(JSON_data); resolve(object) }).catch( (error) => { handle it } );
It seems like the right thing to do to make a more general function to go around this form.
function componentLoader(c_name,resource_url) { Vue.component(c_name, function (resolve, reject) { loader(resource_url). then(function (JSON_data) { var object = transformJSONToJSObject(JSON_data); resolve(object) }).catch( (error) => { handle it } ); }
So, in general, to load a component, we would just need a line like the following:
componentLoader('ThermoPanel','./JSON/thermo-panel.json');
So now, just what is the JSON that is being loaded? It can include everything about the component. In this case, as a panel component, it can include thermometers, machine switches, sliders, gauges, and more. While it seemed nicer to keep the components parts on the web page, it may actually work better to use the subcomponent field that is in the longer example for ‘thermo-panel’ that we made before and also for the other similarly constructed panels. The JSON will contain a complete panel structure.
However, if the reader will notice the inclusion of the function call to transformJSONToJSObject, he will understand that JSON might be coded in some way to make transport easier and to make it easier for a server to handle the definition. After all, the definition will include complete SVG templates, function definitions, and other JavaScript expressions. Also, the JSON object may contain more than just the panel definition because some information may simply aid in bookkeeping or validation. So, one can expect that there will be some treatment of the object upon receipt.
As for the encoding, the data coming in from the server may be encoded in a number of ways. Perhaps it will be simply URL encoded. Or more securely, it might be enciphered. For this discussion, we can just use URL encoding.
Some of the tools that are available for creating Vue applications no doubt take care of the JSON transformation. But, this discussion has so far avoided the use of command line tools. This omission is not that bad as we have also used Vue with the minimum of resources, using only one script tag for the referring to the CDN. However, I certainly do recommend looking into the command line tools especially for organizing projects.
When the JSON arrives at the page, given the component is completely assembled with subcomponents, no more work has to be done to fetch the parts. We can make the assumption that all components will come in fully defined for the rest of this discussion. But, assembling complete component hierarchies will require command line tools at some point.
The SVG editing process will also require some work. The SVG editing processes allow a designer to draw a panel and all the components on it. But, each subcomponent has to be identified, called out in a group, or given a place holder. Any approach to using the drawing requires some treatment of the SVG so that Vue component tags can replace the groups or graphic elements. In this way, any artist rendering can become a template. And, the drawn subcomponents will have to be disassembled into templates for Vue subcomponents.
This sort of parsimony is contrary to the workflow of most of the JavaScript frameworks. The frameworks are about assembling pages. But, editing or drawing, results in something already assembled by an artist. In practice, the result of editing does not provide a text file that corresponds directly to a framework component definition.
More about the editing process may be considered in some other discussion. There is a lot to it. But, for now, we have the tools we need in order to load hierarchal components and make them come alive.
The Lazy Application
For our IoT panel construction, we already have a selection bar that responds to searches. And, we have a way of loading components when we need them. We just need to connect up these parts. And, at last, we have to make sure that the panels appear and that they start working when they do.
The lazy loading of panels done by the async code above provides a sketch of an idea. But, thankfully, some people have experimented to find ways of making sure that all kinds of components can be loaded. There is one codepen entry that shows how to update Vue apps with new components of varying types. That is the mechanism that is needed for updating a designated part of the page with different types of panel.
With the ability to add in different kinds of panels and with a simple mechanism to load their definitions, we can, at last, have our panel searching page.
Here is the HTML that we need in our page so that the Vue app can place components in dynamically:
<template v-for="(panel, index) in panelList"> <component :is="panel" :key="panel.name"></component> </template>
The component tag is a Vue meta tag. See the reference for dynamic components. The properties, special attributes, used for the component tag in this case are is and key. The is attribute exists for dynamic components. And, the key ensures that the new children will have different identities from each other and helps Vue to decide what to draw.
“Children of the same common parent must have unique keys. Duplicate keys will cause rendering errors.”
The template tag will loop through components that are provided in the panelList data field of the application.
So, starting with the application level Vue definition for the icon app, we can make changes to include the panelList in the data elements. (Let’s now call it the panelApp).
var panelApp = new Vue({ el: '#PanelApp', data: { iconList: [ // Where is the data? Still on the server. ], panelList: [ ], queryToken : "Thermo Batches" // picked a name for demo }, methods : { goGetPanel: function (pname) { // var url = panelURL(pname); // this is custom to the site. fetch(url).then((response) => { // this is now browser native response.text().then((text) => { var newData = decodeURIComponent(text); eval(pHat); // widgdef = object def, must be assignment pHat = widgdef; var pnameHat = pname + pcount++; pHat.name = pnameHat; // this is needed for the key this.panelList.push(pHat); // now it’s there. }).catch( error => { /* handle it */ }); } } });
Besides adding in the panel, goGetPanel is now in a form required for getting a component definition from a database or other store. The server side must be careful about delivering JavaScript code in the correct format. As for what the object looks like coming from the server, we have already seen it. It is the kind of object used as a parameter to Vue.component.
Here is the complete body of the Vue app that provides a menu as a search result and a place to put panels fetched from the server when the user clicks an icon.
<div id="PanelApp"> <!-- Recognize the name from the Vue doc --> <div> <h2 itemprop="name">Request MCU Groups</h2> <p itemprop="description">These are groups satistfying this query: .</p> <button onclick="GetIcons(11)">Find All</button> <button onclick="GetIcons(5)">Find 5 Point</button> <button onclick="GetIcons(6)">Find 6 Point</button> </div> <!-- Here is a Vue loop for generating a lit --> <div class="entryart" style="padding:4px"> <button v-for="iconEntry in iconList" @click="goGetPanel(iconEntry.name)" > <div v-html="iconEntry.icon"> </div> </button> </div> <div class="entryart" style="padding:4px" > <template v-for="(panel, index) in panelList"> <component :is="panel" :key="panel.name" :ref="panel.name" ></component> </template> </div> </div>
In the last div, the component tag now has a ref parameter bound to the panel name. The ref parameter allows Vue app to identify which component to update with data and keeps components separate. The ref parameters also allow our application access to the new dynamically loaded components.
In one test version of the panel app, I have the following interval handler:
setInterval(() => { var refall = panelApp.$refs; // all named children that panels for ( var pname in refall ) { // in an object var pdata = refall[pname][0]; // off Vue translation, but it’s there. pdata.temp1 = Math.round(Math.random()*100); // make thermos jump around. pdata.temp2 = Math.round(Math.random()*100); } },2000)
The code provides a little animation, changing thermometers randomly. Each panel has two thermometers, and the app allows the user to keep adding panels. (In the final version, some panels must be thrown away.) The refs are being accessed using panelApp.$refs, a field that Vue creates given the refs information in the component tag.
So, this is what the randomly jumping thermometers look like in one snapshot:
A collection of animated panels for one type of panel (or component). (Large preview)
Connecting The Panel To The IoT Device
So, the last piece of code is a setInterval test updating thermometers with random values every two seconds. But, what we want to do is read in real data from real machines. In order to do that, we will need some form of communication.
There are a variety of ways. But, let’s use MQTT which is a pub/sub message system. Our SPWA can subscribe to messages from devices at any time. When it gets those messages the SPWA can direct each message to the appropriate data handler for the panel mapped to the device identified in the message.
So, basically what we need to do is replace the setInterval with a response handler. And, that will be for one panel. We probably want to map panels to handlers as they are loaded. And, it is up to the web server to see that the correct mapping is delivered.
Once the web server and the SPWA have the page ready for operation, the web server no longer needs to take care of messaging between the page and the device. the MQTT protocol specifies a routing server to handle pub/sub. A number of MQTT servers have been made. Some of them are open source. One very popular one is Mosquito, and there are a few developed on top of Node.js.
The process for the page is simple. The SPWA subscribes to a topic. One good version of a topic is an identifier for an MCU such as a MAC address or a serial number. Or, the SPWA could subscribe to all temperature readings. But, then the page would have to do the work of filtering the messages from all devices. Publication in MQTT is essentially a broadcast or multicast.
Let’s take a look at how the SPWA will interface with MQTT.
Initializing MQTT On The SPWA
There are several client libraries to choose from. One, for instance, is a MQTT.js. Another is eclipse paho. There are more of course. Let’s use Eclipse Paho since it has a CDN stored version. We just need to add the following line to our page:
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
The MQTT client has to connect to a server before it can send and receive messages. So, lines setting up the connection also need to be included in the JavaScript. We can add in a function MQTTinitialize which sets up the client and the responses for connection management and message receipt.
var messagesReady = false; var mqttClient = null; function MQTTinitialize() { mqttClient = new Paho.MQTT.Client(MQTTHostname, Number(MQTTPort), "clientId"); mqttClient.onMessageArrived = onMessageArrived; // connect the client mqttClient.connect({ onSuccess: () => { messagesReady = true; } }); // set callback handlers mqttClient.onConnectionLost = (response) => { // messagesReady = false; // if (response.errorCode !== 0) { console.log("onConnectionLost:"+response.errorMessage); } setTimeout(() => { MQTTinitialize() },1000); // try again in a second }; }
Setting up Subscription
With the connection ready, the client can subscribe to message channels, send messages on them, etc. Just a few routines can do most of the work necessary to connect panels with the MQTT pathways.
For the panel SPWA, the moment of subscription can be used to establish the association between the panel and the topic, the MCU identifier.
function panelSubcription(topic,panel) { gTopicToPanel[topic] = panel; gPanelToTopic[panel] = topic; mqttClient.subscribe(topic); }
Given that an MCU is publishing on its topic, the SPWA will receive a message. Here, the Paho message is unpacked. And, then the message is passed on into the application mechanics.
function onMessageArrived(pmessage) { // var topic = pmessage.destinationName; var message = pmessage.payloadString; // var panel = gTopicToPanel[topic]; deliverToPanel(panel,message); }
So, now all we need to do is create deliverToPanel which should be somewhat like the interval handler that we had before. However, the panel is clearly identified, and only the keyed data sent in the particular message may be updated.
function deliverToPanel(panel,message) { var refall = panelApp.$refs; // all named children that panels var pdata = refall[panel][0]; // off Vue translation, but it’s there. var MCU_updates = JSON.parse(message); for ( var ky in MCU_updates ) { pdata[ky] = MCU_updates[ky] } }
This deliverToPanel function is abstract enough to allow any panel definition with any number of data points for animation.
Sending Messages
To complete the application loop between the MCU and the SPWA, we define a function to send a message.
function sendPanelMessage(panel,message) { var topic = gPanelToTopic[panel]; var pmessage = new Paho.MQTT.Message(message); pmessage.destinationName = topic; mqttClient.send(pmessage); }
The sendPanelMessage function does no more than sending the message out on the same topic pathway that the SPWA subscribes to.
As we plan to make the icon buttons responsible for bringing in some number of panels for a single cluster of MCU’s, there will be more than one panel to take care of. But, we keep in mind that each panel corresponds to a single MCU, so we have a one-one mapping, for which we may use two JavaScript maps for the map and the inverse.
So, when do we send messages? Usually, the panel application will send a message when it wants to change the state of the MCU.
Keeping The View (Vue) State In Sync With Devices
One of the great things about Vue is that it is very easy to keep the data model synchronized with the activity of the user, who may edit fields, click on buttons, use sliders, etc. One can be sure that button and field changes will be reflected immediately in the components’ data fields.
But, we want changes to fire off messages to the MCU as soon as the changes occur. So, we seek to make use of the interface events that Vue may govern. We seek to respond to such an event, but only after the Vue data model is ready with the current value.
I created another kind of panel, this one with a fairly artistic looking button (perhaps inspired by Jackson Pollock). And, I went about turning it into something whose click reports the state back to the panel that contains it. That was not so simple a process.
One thing that threw me off is that I had forgotten some of the oddities in managing SVG. I first tried to change the style string so that the display field of the CSS style would either be “None” or “something”. But, the browser never rewrote the styles string. But, as that was cumbersome, I tried changing the CSS class. That also had no effect. But, there the visibility attribute, which most of us recall from old HTML (version 1.0 perhaps), but that is very up to date in SVG. And, that works well. All, I had to do was to get the button click event to propagate.
Vue has designed properties to propagate in one direction, parent to child. So, to change data in the application, or in the panel, you have to send a change event to the parent. Then, you can change the data. The change of the data element controlling the button causes Vue to update the property affecting the visibility of the SVG element we have chosen to indicate state. Here is an example:
Finally, a collection of different types of panels each with instances assigned to separate MCU’s. (Large preview)
Each instance of the squiggly button panel is independent. So, some are ON and some are OFF.
This snippet of SVG contains the odd-looking yellow indicator:
<path :visibility="stateView" style="opacity:0.98000004;fill:#faea4a;fill-opacity:1;stroke:#eecd5c;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="sunthing" d="m -36.544616,12.266886 c 19.953088,17.062165 5.07961,-19.8251069 5.317463,8.531597 0.237853,28.356704 13.440044,-8.847959 -3.230451,10.779678 -16.670496,19.627638 14.254699,-2.017715 -11.652451,3.586456 -25.90715,5.60417 10.847826,19.889979 -8.095928,-1.546575 -18.943754,-21.436555 -1.177383,14.210702 -4.176821,-12.416207 -2.999438,-26.6269084 -17.110198,8.030902 2.14399,-8.927709 19.254188,-16.9586105 -19.075538,-8.0837048 9.448721,-5.4384245 28.52426,2.6452804 -9.707612,-11.6309807 10.245477,5.4311845 z" transform="translate(78.340803,6.1372042)" />
The visibility is populated by stateView, a computed variable that maps the state boolean to a string for SVG.
Here is the panel component definition template:
<script type="text/x-template" id="mcu-control-panel-template"> <div> <control-switch :state="bstate" v-on:changed="saveChanges" ></control-switch> <gauge :level="fluidLevel" ></gauge> </div> </script>
And, this is the JavaScript definition of the Vue panel with its children as subcomponents:
var widgdef = { data: function () { var currentPanel = { // at the top level, values controlling children bstate : true, fluidLevel : Math.round(Math.random()*100) } // return currentPanel }, template: '#mcu-control-panel-template', methods: { saveChanges: function() { // in real life, there is more specificity this.bstate = !this.bstate relayToMCU(this.name,"button",this.bstate) // to be defined } }, components: { 'control-switch' : { // the odd looking button props: [’state'], template: '#control-switch-template', // for demo it is in the page. computed: { // you saw this in the SVG above. stateView : function() { return ( this.state ) ? "visible" : "hidden" } }, methods : { // the button handler is in the SVG template at the top. stateChange : function () { // can send this.$emit('changed'); // tell the parent. See on the template instance } } }, 'gauge' : { // some other nice bit of SVG props: ['level'], template: '#gauge-template' } } }
So, now the mechanism for a single button embedded in a panel has been laid out. And, there has to be a hook for telling the MCU that something has taken place. It must be called immediately after the data state of the panel component has been updated. Let’s define it here:
function relayToMCU(panel,switchName,bstate) { var message = switchName + ':' + bstate // a on element parameter string. sendPanelMessage(panel,message) }
There is the state change on it’s way to hardware in just two lines of code.
But, this is a fairly simple case. Any switch can be viewed as a function call to a piece of hardware out in the world. So, the string might contain the switch name and several other data elements. So, the component method that registers change will have to have some custom handling in it in order that it might gather together all the pieces of data set on the panel and send them along in one command string. Even the command string is a little simple. If the MCU is quite small, the command string might have to be translated into a code. If the MCU has a great deal of capability, the command string might actually be a JSON structure or perhaps all the data that the panel hosts.
In this discussion, the buttons on the icon panel contain the name of the panel to fetch. That may also be fairly simplified as well. It seems to make sense that that parameter can stand for any panel that might be stored in an enterprises databases. But, perhaps it is some formula. Perhaps, information about the panel should be wrapped around the panel definition that we receive from the server. In any case, the basics can be easily expanded upon once certain headaches are out of the way, like making the SVG respond to clicks properly.
Conclusion
This discussion has laid out some basic steps and decisions that lead to the realization of a Single Page Web App (SPWA) that can interface with IoT devices. We now know how to get panels from a web server and turn them into MCU interface.
There is much more to this discussion with quite a few other discussions that may follow. Starting with Vue is one thing to think about. But, then there is the whole MCU story, which we have only briefly touched upon.
In particular, by selecting MQTT as a communication substrate, we assume that IoT devices on the other end can somehow be ruled by MQTT. But, that may not always be the case. Sometimes gateways are needed if MQTT is to gain access to a device with serial links or Bluetooth. Or, perhaps all one ever needs from on the web page is WebSockets. Nevertheless, we used MQTT as an example to show how Vue could both receive and send data while keeping its data state in sync with devices.
Once again we only have part of the story. This time it is for synchronization because the page should be able to deal with alerts and bother the user if something critical is happening. Sometimes messages can get lost. So, we have to have a mechanism for acknowledgments.
Finally, it is my opinion that Vue makes updating data upon receipt quite elegant. But, sending the state changes is not so straight forward. It does not seem to make the job much simpler than can be done with vanilla JavaScript. But, there is a way and it makes sense.
Perhaps a clean library can be built to make a universal set of components for all panels. The elements for making such libraries and having them stored in a database have been briefly mentioned. Tools that go beyond just making SVG pictures may have to be developed. In any case, there are likely many things that can be done for the next steps.
(dm, yk, il)
0 notes
Text
Write a reusable Blazor component
In this blog I have already talked about creating components but now I want to start from the beginning and explain how to write a reusable Blazor component from scratch. This is the first part of a Blazor article series to tell how to create a reusable Blazor component. So, we can use them in our Blazor application independently of the hosting model (Web Assembly or Server-side). If you google a bit, you can find a lot of example like that but I can’t find one example that explain exactly how the interaction works. Here, step by step what I understood.
Then, for more documentation, example and components about Blazor, here same links in this blog:
Getting Started With C# And Blazor
Setting Up A Blazor WebAssembly Application
Working With Blazor’s Component Model
Secure Blazor WebAssembly With IdentityServer4
Blazor Using HttpClient With Authentication
InputSelect component for enumerations in Blazor
Use LocalStorage with Blazor WebAssembly
Modal Dialog component for Blazor
Create Tooltip component for Blazor
Consume ASP.NET Core Razor components from Razor class libraries | Microsoft Docs
Also, you can download the source code of this post from GitHub.
So, there result of this post is a simple button. When you click the button, Blazor calls the code in a javascript file and runs it. The result is passed to the C# code and then as result of the component.
Start!
First, I have to create a new Blazor project. For that, Create a new project and search for Blazor. Then, select Blazor WebAssembly App.
Visual Studio 2019 – Create a new project
Then, you have to select the name of the project and its location. The name of my project is PSCBlazor and you can find it on GitHub.
Configure a new project
Select as Target Framework .NET 5. Then, Create.
Addition Information
Now, we have the main project. But to write a reusable Blazor component, we have to create another project for it. Following the same procedure, create a new project but this time select Razor Class Library.
Create a Razor Class Library
Then, follow the instruction. The name of my project is PSCBlazor.SimpleComponent.
If you don’t want to use Visual Studio, you can run from the Command Line
dotnet new razorclasslib
By default, the created project contains several elements:
A predefined “Component1” that is a basic component displaying some styled text in Component1.razor.
A ExampleJsInterop class that demonstrate how to call a basic JS function defined in its own js file in ExampleJsInterop.cs.
The static resources (like png, css and js files) in the “wwwroot” folder: – background.png – exampleJsInterop.js – styles.css
The solution for the custom component
Separate the code from the razor code
Behind the scenes, when you write a razor page, an actual C# class is generated and hopefully the class is partial. It means that it is easy to separate the behavioral code of your component from the html view code.
Single mixed file Component1.razor:
<div class="my-component"> This Blazor component is defined in the <strong>PSCBlazor.SimpleComponent</strong> package. </div> @code { }
Sometimes it is nice to separate the view from the code. In Visual Studio is pretty simple. Create a new Class with the full name of the view file plus cs. For example, if the view is Component1.razor the new file should be Component.razor.cs and automatically Visual Studio understands that this file is part of the view.
Split view and code
Then, a little change in the code because the new class must be a partial class. For example:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PSCBlazor.SimpleComponent { public partial class Component1 { } }
Interop between Blazor and JavaScript
Now, I write a reusable component for Blazor and I’m going to modify the component to be able to exchange some data between the component itself and the parent. For that, I have to use a JS function through the generated ExampleJsInterop to prompt the user for a text input.
The component will be used this way with TextValue being a property used in the binding:
<Component1 ParentName=”Some text I want to transmit from the parent to the child component” @bind-Text=”TextValue”> </Component1>
Define the parameters
We need to define a property ParentName to transmit some data from the parent to the child component:
[Parameter] public string ParentName { get; set; }
The Parameter attribute tells that the C# property can be used as component parameter.
Now, I need to define a parameter to be able to transmit data to the child component, but we also want to get data from the component using a @bind property.
To make it possible, I have to define a parameter like the previous one named in our case Text.
[Parameter] public string Text { get; set; }
Define a EventCallback parameter to be able to notify the binding engine that the value has changed. As a convention, the name of the event callback property must be named accordingly to the property you want to bind. In our case it must be named TextChanged.
[Parameter] public EventCallback<string> TextChanged { get; set; }
The name of the event callback can be named differently but in that case it is required to specify it when binding a value using
@bind-{Property}:event=”NameOfPropertyChanged”
It means that the two notation are equivalent when binding a value:
<Child @bind-MyProperty="Text" /> <Child @bind-MyProperty="Text" @bind-MyProperty:event="TextChanged" />
Once the properties are defined, the component will have to invoke the event callback when the property value is changing.
await TextChanged.InvokeAsync(valueOfUserTextInputFromModalDialog);
Inject the appropriate services
To use the Java Script interop and the ExampleJsInterop example class we have to use an injected instance of IJSRuntime. We can do it with a dedicated attribute Inject in the component C# file.
Implement the behavior
Now, I have all we need to implement the behavior by adding a button with a click handler that is going to call the JS function through the ExampleJsInterop.Prompt method. Once we got the result we just need to forward to the TextChanged event call back and to update our Text property value.
So, in the razor file I created a bit of HTML, just to have a bit more fun.
<div class="my-component"> <div class="my-buttonkeeper"> <button class="my-button" @onclick="GetTextAsync">Click me</button> </div> </div> <br /> <text>Text (in the component) : @Text</text> <br />
Then, in the Component1.razor.css I added some CSS code
.my-component { border: 2px dashed red; padding: 1em; margin: 1em 0; background-image: url('background.png'); } .my-buttonkeeper { text-align: center; } .my-button { width: 150px; height: 150px; background-color: green; color: white; font-size: 20px; }
Why? Because I want to be sure the main application adds the CSS from this component but also the javascript we need. I’ll explain that in a second.
Then, it is the moment of the CS file:
private async Task GetTextAsync() { // Call JS interop. var text = await new ExampleJsInterop(JsRuntime).Prompt("Enter some text:"); // Trigger the changed event. await TextChanged.InvokeAsync(text); // Set the property value. Text = text; }
Inside wwwroot directory we have ready to use exampleJsInterop.js file which was created by Visual Studio or CLI. The code is like that
// This is a JavaScript module that is loaded on demand. It can export any number of // functions, and may import other JavaScript modules if required. export function showPrompt(message) { return prompt(message, 'Type anything here'); }
This is a simple javascript function. This rings a bell to me, it is like in React. Now, the magic is happening in the ExampleJsInterop.cs
using Microsoft.JSInterop; using System; using System.Threading.Tasks; namespace PSCBlazor.SimpleComponent { // This class provides an example of how JavaScript functionality can be wrapped // in a .NET class for easy consumption. The associated JavaScript module is // loaded on demand when first needed. // // This class can be registered as scoped DI service and then injected into Blazor // components for use. public class ExampleJsInterop : IAsyncDisposable { private readonly Lazy<Task<IJSObjectReference>> moduleTask; public ExampleJsInterop(IJSRuntime jsRuntime) { moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>( "import", "./_content/PSCBlazor.SimpleComponent/exampleJsInterop.js").AsTask()); } public async ValueTask<string> Prompt(string message) { var module = await moduleTask.Value; return await module.InvokeAsync<string>("showPrompt", message); } public async ValueTask DisposeAsync() { if (moduleTask.IsValueCreated) { var module = await moduleTask.Value; await module.DisposeAsync(); } } } }
So, between the lines 24 and 28 there is a call via IJSObjectReference to the javascript function in the file under the wwwroot. You can see that in line 21 where there is an import of the javascript file.
Run the demo project
So, almost finish to write a reusable Blazor component with interop with javascript. First, I have to add the component as dependency in the project. In Solution Explorer, right click on Dependecies in the PSCBlazor and click on Add project references… Then, select the component.
Reference manager
Now, open _Imports.razor and add the reference to this component to add this line
@using PSCBlazor.SimpleComponent
Then, open Index.razor and add this code
<Component1 ParentName="This is a test" @bind-Text="_text"></Component1> The text from the component is: @_text @code { private string _text = ""; }
Also, we want to add the CSS and the javascript for the magic. Depending on the hosting model, it is required to add the reference to the CSS and javascript in specific index files in the application project:
wwwroot/index.html for the Web Assembly hosting model.
and
Pages/_Host.cshtml for the Server-side hosting model.
So, open the index.html under wwwroot and add those lines (I deleted a lot of lines for the purpose of showing what you have to add):
<head> <link href="_content/PSCBlazor.SimpleComponent/styles.css" rel="stylesheet" /> </head> <body> <script src="_framework/blazor.webassembly.js"></script> <script src="_content/PSCBlazor.SimpleComponent/exampleJsInterop.js" /> </body>
Basically, in the head I have to add the reference to the CSS from the component. Similarly, I have to do the same to add the javascript.
Compile and run. If everything is ok, you have the same result as I show at the top of this post.
The post Write a reusable Blazor component appeared first on PureSourceCode.
from WordPress https://www.puresourcecode.com/dotnet/blazor/write-a-reusable-blazor-component/
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
Implementing Dark Mode In React Apps Using styled-components
About The Author
Blessing Krofegha is a Software Engineer Based in Lagos Nigeria, with a burning desire to contribute to making the web awesome for all, by writing and building … More about Blessing …
Light mode is a convention in most web and mobile apps. However, in modern development, we have seen how dark mode, which displays light text and interface elements on a dark background, is quickly becoming a user preference. In this article, we’ll learn how to efficiently implement dark mode in a React app on a simple web page, using the styled-components library and leveraging some React features like hooks. We will also discuss the pros and cons of dark mode and why it should be adopted.
One of the most commonly requested software features is dark mode (or night mode, as others call it). We see dark mode in the apps that we use every day. From mobile to web apps, dark mode has become vital for companies that want to take care of their users’ eyes.
Dark mode is a supplemental feature that displays mostly dark surfaces in the UI. Most major companies (such as YouTube, Twitter, and Netflix) have adopted dark mode in their mobile and web apps.
While we won’t go in depth into React and styled-components, a basic knowledge of React, CSS, and styled-components would come in handy. This tutorial will benefit those who are looking to enhance their web applications by catering to those who love dark mode.
StackOverflow announces dark mode on Twitter (Large preview)
A few days before the writing of this article, StackOverflow announced its release of dark mode, giving users the chance to toggle between the two modes.
Dark mode reduces eye strain and helps when you’re working for a long time on a computer or mobile phone.
What Is Dark Mode?
Dark mode is the color scheme of any interface that displays light text and interface elements on a dark background, which makes the screen a little easier to look at mobile phones, tablets, and computers. Dark mode reduces the light emitted by the screen, while maintaining the minimum color-contrast ratios required for readability.
Why Should You Care About Dark Mode?
Dark mode enhances visual ergonomics by reducing eye strain, adjusting the screen to current light conditions, and providing ease of use at night or in dark environments.
Before implementing dark mode in our app, let’s look at its benefits.
Battery Saving
Dark mode in web and mobile apps can prolong the battery life of a device. Google has confirmed that dark mode on OLED screens has been a huge help to battery life.
For example, at 50% brightness, dark mode in the YouTube app saves about 15% more screen energy than a flat white background. At 100% screen brightness, the dark interface saves a whopping 60% of screen energy.
Dark Mode Is Beautiful
Dark mode is beautiful, and it can significantly enhance the appeal of the screen.
While most products are going for that similar bland white look, dark mode offers something different that feels mysterious and new.
It also provides great opportunities to present graphic content such as dashboards, pictures, and photos in a fresh way.
The beauty of Twitter’s dark mode over light mode (Large preview)
Now that you know why you should implement dark mode in your next web app, let’s dive deep into styled-components, which is the defining resource of this tutorial.
Dark mode is the color scheme of any interface that displays light text and interface elements on a dark background, which makes it a little easier to look at on mobile phones, tablets, and computers.
What Are styled-components?
Throughout this article, we will be using the styled-components library very often. There have always been many ways to style a modern web app. There’s the traditional method of styling at the document level, which includes creating an index.css file and linking it to the HTML or styling inside the HTML file.
A lot has changed in the ways that web apps are styled recently, since the introduction of CSS-in-JS.
CSS-in-JS refers to a pattern in which CSS is composed using JavaScript. It utilizes tagged template literals to style components in a JavaScript file.
To learn more about CSS-in-JS, check out Anna Monus’s article on the subject.
styled-components is a CSS-in-JS library lets you use all of the features of CSS that you love, including media queries, pseudo-selectors, and nesting.
Why styled-components?
styled-components was created for the following reasons:
No class name hell Instead of you scratching your head to find a class name for an element, styled-components generates unique class names for your styles. You’ll never have to worry about misspellings or using class names that have no meaning.
Using props styled-components allow us to extend styling properties using the props parameter, commonly used in React — thus, dynamically affecting the feel of a component via the application’s state.
Supports Sass syntax Writing Sass syntax out of the box without having to set up any preprocessors or extra build tools is possible with styled-components. In your style definitions, you can use the & character to target the current component, use pseudo-selectors, and experiment with nesting.
Theming styled-components have full theming support by exporting a ThemeProvider wrapper component. This component provides a theme to all React components within itself via the Context API. In the rendering tree, all styled-components will have access to the provided theme, even when they are multiple levels deep. As we continue in this tutorial, we will look deeper into the theming features of styled-components.
To learn more advantages of styled-components, check out Kris Guzman’s article.
Implementing Dark Mode
In this article, we are going to implement dark mode on a simple YouTube-like web page.
To follow along, ensure that you clone the original repository from the starter branch.
Setting Up
Let’s install all of the dependencies in our package.json file. From the terminal, run the following command:
npm install
Upon its successful installation, run npm start. Here is what the web page looks like without dark mode implemented on it.
The web page to be used, without dark mode. (Large preview)
To install styled-components, in your terminal run npm install styled-components.
Implementation
To implement dark mode, we need to create four different components.
Theme This contains the color properties of our light and dark themes.
GlobalStyles This contains the global styles for the entire document.
Toggler This holds the button element that toggles the functionality.
useDarkMode This custom hook handles the logic behind the change of theme and the persistence of our theme in localStorage.
Theme Component
In the src folder, you’ll see components in the components folder. Create a Themes.js file, and add the following code to it.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
Here, we’ve defined and exported lightTheme and darkTheme objects with distinct color variables. Feel free to experiment and customize the variables to suit you.
globalStyles Component
Remaining in your components folder, create a globalStyles.js file, and add the following code:
import { createGlobalStyle} from "styled-components" export const GlobalStyles = createGlobalStyle` body { background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif; transition: all 0.50s linear; } `
We’ve imported createGlobalStyle from styled-components. The createGlobalStyle method replaces the now deprecated injectGlobal method from styled-components version 3. This method generates a React component, which, when added to your component tree, will inject global styles into the document, in our case, App.js.
We defined a GlobalStyle component and assigned background and color properties to values from the theme object. Thus, every time we switch the toggle, the values will change depending on the dark theme or light theme objects that we are passing to ThemeProvider (which will be created later, as we proceed).
The transition property of 0.50s enables this change to occur a little more smoothly, so that as we toggle back and forth, we can see the changes happen.
Creating Theme-Toggling Functionality
To implement the theme-toggling functionality, we need to add only a few lines of code. In the App.js file, add the following code (note that the highlighted code is what you should add):
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') } useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return ( <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/> <div className="App"> <button onClick={themeToggler}>Switch Theme</button> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
The highlighted code is the one newly added to App.js. We’ve imported ThemeProvider from styled-components. ThemeProvider is a helper component in the styled-components library that provides theming support. This helper component injects a theme into all React component below itself via the Context API.
In the rendering tree, all styled-components will have access to the provided theme, even when they are multiple levels deep. Check out the section on “Theming”.
Next, we import the GlobalStyle wrapper from ./components/Globalstyle. Lastly, from the top, we import both the lightTheme and darkTheme objects from ./components/Themes.
In order for us to create a toggling method, we need a state that holds our theme’s initial color value. So, we create a theme state, and set the initial state to light, using the useState hook.
Now, for the toggling functionality.
The themeToggler method uses a ternary operator to check the state of the theme, and it toggles either dark or light based on the value of the condition.
ThemeProvider, a styled-components helper component, wraps everything in the return statement and injects any components below it. Remember that our GlobalStyles inject global styles into our components; hence, it’s called inside the ThemeProvider wrapper component.
Lastly, we created a button with an onClick event that assigns our themeToggler method to it.
Let’s see the outcome thus far.
Dark mode implemented without persistence (Large preview)
Our App.js file needs to be refactored; a lot of its code is not DRY. (DRY stands for “don’t repeat yourself”, a basic principle of software development aimed at reducing repetition.) All of the logic seems to be in App.js; it’s good practice to separate our logic for the sake of clarity. So, we’ll create a component that handles the toggling functionality.
Toggle Component
Still within the components folder, create a Toggler.js file, and add the following code to it:
import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle;
To keep things neat, we’ve styled our toggle button in the Toggle component, using the styled function from styled-components.
This is purely for presentation; you can style the button as you see fit.
Inside the Toggle component, we pass two props:
the theme provides the current theme (light or dark);
the toggleTheme function will be used to switch between themes.
Next, we return the Button component and assign a toggleTheme function to the onClick event.
Lastly, we use propTypes to define our types, ensuring that our theme is a string and isRequired, while our toggleTheme is func and isRequired.
Using Custom Hooks (useDarkMode)
When building an application, scalability is paramount, meaning that our business logic must be reusable, so that we can use it in many places and even in different projects.
That is why it would be great to move our toggling functionality to a separate component. For that, we would create our own custom hook.
Let’s create a new file named useDarkMode.js in the components folder, and move our logic to this file, with some tweaks. Add the following code to the file:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };
We’ve added a few things here.
setMode We use localStorage to persist between sessions in the browser. So, if a user has chosen the dark or light theme, that’s what they’ll get upon their next visit to the app or if they reload the page. Hence, this function sets our state and passes theme to localStorage.
themeToggler This function uses a ternary operator to check the state of the theme and toggles either dark or light based on the truth of the condition.
useEffect We’ve implemented the useEffect hook to check on component mounting. If the user has previously selected a theme, we will pass it to our setTheme function. In the end, we will return our theme, which contains the chosen theme and the themeToggler function to switch between modes.
I think you’ll agree that our dark-mode component looks sleek.
Let’s head over to App.js for the final touches.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
The highlighted code is newly added to App.js.
First, we import our custom hook, destructure the theme and themeToggler props, and set it with the useDarkMode function.
Note that the useDarkMode method replaces our theme state, which was initially in App.js.
We declare a themeMode variable, which renders either a light or dark theme based on the condition of the theme mode at the time.
Now, our ThemeProvider wrapper component is assigned our just recently created themeMode variable to the theme prop.
And lastly, in place of the regular button, we pass in the Toggle component.
Remember that in our Toggle component, we defined and styled a button and passed both theme and toggleTheme to them as props. So, all we have to do is pass these props appropriately to the Toggle component, which will act as our button in App.js.
Yes! Our dark mode is set, and it persists, not changing color when the page is refreshed or visited in a new tab.
Let’s see the outcome in action:
Dark mode implemented, but with a glitch in the button color when the browser reloads. (Large preview)
Almost everything works well, but there is one small thing we can do to make our experience splendid. Switch to the dark theme and then reload the page. Do you see that the blue color in the button loads before the gray for a brief moment? That happens because our useState hook initiates the light theme initially. After that, useEffect runs, checks localStorage, and only then sets the theme to dark. Let’s jump over to our custom hook useDarkMode.js and add a little code:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const [mountedComponent, setMountedComponent] = useState(false) const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light') setMountedComponent(true) }, []); return [theme, themeToggler, mountedComponent] };
The highlighted code is the only one added to useDarkMode.js. We’ve created another state named mountedComponent and set the default value to false using the useState hook. Next, inside the useEffect hook, we set the mountedComponent state to true using setMountedComponent. Lastly, in the return array, we include the mountedComponent state.
Finally, let’s add a bit of code in App.js to make it all work.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, themeToggler, mountedComponent] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); if(!mountedComponent) return <div/> return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
We’ve added our mountedComponent state as a prop in our useDarkMode hook, and we’ve checked whether our component has mounted, because this is what happens in the useEffect hook. If it hasn’t happened yet, then we will render an empty div.
Let’s see the outcome of our dark-mode web page.
Final result of dark mode (Large preview)
Now, you’ll notice that while in dark mode, when the page reloads, the button’s color doesn’t change.
Conclusion
Dark mode is increasingly becoming a user preference, and implementing it in a React web app is a lot easier when using the ThemeProvider theming wrapper in styled-components. Go ahead and experiment with styled-components as you implement dark mode; you could add icons instead of a button.
Please do share your feedback and experience with the theming feature in styled-components in the comments section below. I’d love to see what you come up with!
The supporting repository for this article is available on GitHub. Also, check it out on CodeSandbox.
References
(ks, ra, il, al)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/implementing-dark-mode-in-react-apps-using-styled-components/ source https://scpie.tumblr.com/post/616690895071379456
0 notes
Text
Implementing Dark Mode In React Apps Using styled-components
About The Author
Blessing Krofegha is a Software Engineer Based in Lagos Nigeria, with a burning desire to contribute to making the web awesome for all, by writing and building … More about Blessing …
Light mode is a convention in most web and mobile apps. However, in modern development, we have seen how dark mode, which displays light text and interface elements on a dark background, is quickly becoming a user preference. In this article, we’ll learn how to efficiently implement dark mode in a React app on a simple web page, using the styled-components library and leveraging some React features like hooks. We will also discuss the pros and cons of dark mode and why it should be adopted.
One of the most commonly requested software features is dark mode (or night mode, as others call it). We see dark mode in the apps that we use every day. From mobile to web apps, dark mode has become vital for companies that want to take care of their users’ eyes.
Dark mode is a supplemental feature that displays mostly dark surfaces in the UI. Most major companies (such as YouTube, Twitter, and Netflix) have adopted dark mode in their mobile and web apps.
While we won’t go in depth into React and styled-components, a basic knowledge of React, CSS, and styled-components would come in handy. This tutorial will benefit those who are looking to enhance their web applications by catering to those who love dark mode.
StackOverflow announces dark mode on Twitter (Large preview)
A few days before the writing of this article, StackOverflow announced its release of dark mode, giving users the chance to toggle between the two modes.
Dark mode reduces eye strain and helps when you’re working for a long time on a computer or mobile phone.
What Is Dark Mode?
Dark mode is the color scheme of any interface that displays light text and interface elements on a dark background, which makes the screen a little easier to look at mobile phones, tablets, and computers. Dark mode reduces the light emitted by the screen, while maintaining the minimum color-contrast ratios required for readability.
Why Should You Care About Dark Mode?
Dark mode enhances visual ergonomics by reducing eye strain, adjusting the screen to current light conditions, and providing ease of use at night or in dark environments.
Before implementing dark mode in our app, let’s look at its benefits.
Battery Saving
Dark mode in web and mobile apps can prolong the battery life of a device. Google has confirmed that dark mode on OLED screens has been a huge help to battery life.
For example, at 50% brightness, dark mode in the YouTube app saves about 15% more screen energy than a flat white background. At 100% screen brightness, the dark interface saves a whopping 60% of screen energy.
Dark Mode Is Beautiful
Dark mode is beautiful, and it can significantly enhance the appeal of the screen.
While most products are going for that similar bland white look, dark mode offers something different that feels mysterious and new.
It also provides great opportunities to present graphic content such as dashboards, pictures, and photos in a fresh way.
The beauty of Twitter’s dark mode over light mode (Large preview)
Now that you know why you should implement dark mode in your next web app, let’s dive deep into styled-components, which is the defining resource of this tutorial.
Dark mode is the color scheme of any interface that displays light text and interface elements on a dark background, which makes it a little easier to look at on mobile phones, tablets, and computers.
What Are styled-components?
Throughout this article, we will be using the styled-components library very often. There have always been many ways to style a modern web app. There’s the traditional method of styling at the document level, which includes creating an index.css file and linking it to the HTML or styling inside the HTML file.
A lot has changed in the ways that web apps are styled recently, since the introduction of CSS-in-JS.
CSS-in-JS refers to a pattern in which CSS is composed using JavaScript. It utilizes tagged template literals to style components in a JavaScript file.
To learn more about CSS-in-JS, check out Anna Monus’s article on the subject.
styled-components is a CSS-in-JS library lets you use all of the features of CSS that you love, including media queries, pseudo-selectors, and nesting.
Why styled-components?
styled-components was created for the following reasons:
No class name hell Instead of you scratching your head to find a class name for an element, styled-components generates unique class names for your styles. You’ll never have to worry about misspellings or using class names that have no meaning.
Using props styled-components allow us to extend styling properties using the props parameter, commonly used in React — thus, dynamically affecting the feel of a component via the application’s state.
Supports Sass syntax Writing Sass syntax out of the box without having to set up any preprocessors or extra build tools is possible with styled-components. In your style definitions, you can use the & character to target the current component, use pseudo-selectors, and experiment with nesting.
Theming styled-components have full theming support by exporting a ThemeProvider wrapper component. This component provides a theme to all React components within itself via the Context API. In the rendering tree, all styled-components will have access to the provided theme, even when they are multiple levels deep. As we continue in this tutorial, we will look deeper into the theming features of styled-components.
To learn more advantages of styled-components, check out Kris Guzman’s article.
Implementing Dark Mode
In this article, we are going to implement dark mode on a simple YouTube-like web page.
To follow along, ensure that you clone the original repository from the starter branch.
Setting Up
Let’s install all of the dependencies in our package.json file. From the terminal, run the following command:
npm install
Upon its successful installation, run npm start. Here is what the web page looks like without dark mode implemented on it.
The web page to be used, without dark mode. (Large preview)
To install styled-components, in your terminal run npm install styled-components.
Implementation
To implement dark mode, we need to create four different components.
Theme This contains the color properties of our light and dark themes.
GlobalStyles This contains the global styles for the entire document.
Toggler This holds the button element that toggles the functionality.
useDarkMode This custom hook handles the logic behind the change of theme and the persistence of our theme in localStorage.
Theme Component
In the src folder, you’ll see components in the components folder. Create a Themes.js file, and add the following code to it.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
Here, we’ve defined and exported lightTheme and darkTheme objects with distinct color variables. Feel free to experiment and customize the variables to suit you.
globalStyles Component
Remaining in your components folder, create a globalStyles.js file, and add the following code:
import { createGlobalStyle} from "styled-components" export const GlobalStyles = createGlobalStyle` body { background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif; transition: all 0.50s linear; } `
We’ve imported createGlobalStyle from styled-components. The createGlobalStyle method replaces the now deprecated injectGlobal method from styled-components version 3. This method generates a React component, which, when added to your component tree, will inject global styles into the document, in our case, App.js.
We defined a GlobalStyle component and assigned background and color properties to values from the theme object. Thus, every time we switch the toggle, the values will change depending on the dark theme or light theme objects that we are passing to ThemeProvider (which will be created later, as we proceed).
The transition property of 0.50s enables this change to occur a little more smoothly, so that as we toggle back and forth, we can see the changes happen.
Creating Theme-Toggling Functionality
To implement the theme-toggling functionality, we need to add only a few lines of code. In the App.js file, add the following code (note that the highlighted code is what you should add):
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') } useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return ( <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/> <div className="App"> <button onClick={themeToggler}>Switch Theme</button> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
The highlighted code is the one newly added to App.js. We’ve imported ThemeProvider from styled-components. ThemeProvider is a helper component in the styled-components library that provides theming support. This helper component injects a theme into all React component below itself via the Context API.
In the rendering tree, all styled-components will have access to the provided theme, even when they are multiple levels deep. Check out the section on “Theming”.
Next, we import the GlobalStyle wrapper from ./components/Globalstyle. Lastly, from the top, we import both the lightTheme and darkTheme objects from ./components/Themes.
In order for us to create a toggling method, we need a state that holds our theme’s initial color value. So, we create a theme state, and set the initial state to light, using the useState hook.
Now, for the toggling functionality.
The themeToggler method uses a ternary operator to check the state of the theme, and it toggles either dark or light based on the value of the condition.
ThemeProvider, a styled-components helper component, wraps everything in the return statement and injects any components below it. Remember that our GlobalStyles inject global styles into our components; hence, it’s called inside the ThemeProvider wrapper component.
Lastly, we created a button with an onClick event that assigns our themeToggler method to it.
Let’s see the outcome thus far.
Dark mode implemented without persistence (Large preview)
Our App.js file needs to be refactored; a lot of its code is not DRY. (DRY stands for “don’t repeat yourself”, a basic principle of software development aimed at reducing repetition.) All of the logic seems to be in App.js; it’s good practice to separate our logic for the sake of clarity. So, we’ll create a component that handles the toggling functionality.
Toggle Component
Still within the components folder, create a Toggler.js file, and add the following code to it:
import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle;
To keep things neat, we’ve styled our toggle button in the Toggle component, using the styled function from styled-components.
This is purely for presentation; you can style the button as you see fit.
Inside the Toggle component, we pass two props:
the theme provides the current theme (light or dark);
the toggleTheme function will be used to switch between themes.
Next, we return the Button component and assign a toggleTheme function to the onClick event.
Lastly, we use propTypes to define our types, ensuring that our theme is a string and isRequired, while our toggleTheme is func and isRequired.
Using Custom Hooks (useDarkMode)
When building an application, scalability is paramount, meaning that our business logic must be reusable, so that we can use it in many places and even in different projects.
That is why it would be great to move our toggling functionality to a separate component. For that, we would create our own custom hook.
Let’s create a new file named useDarkMode.js in the components folder, and move our logic to this file, with some tweaks. Add the following code to the file:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };
We’ve added a few things here.
setMode We use localStorage to persist between sessions in the browser. So, if a user has chosen the dark or light theme, that’s what they’ll get upon their next visit to the app or if they reload the page. Hence, this function sets our state and passes theme to localStorage.
themeToggler This function uses a ternary operator to check the state of the theme and toggles either dark or light based on the truth of the condition.
useEffect We’ve implemented the useEffect hook to check on component mounting. If the user has previously selected a theme, we will pass it to our setTheme function. In the end, we will return our theme, which contains the chosen theme and the themeToggler function to switch between modes.
I think you’ll agree that our dark-mode component looks sleek.
Let’s head over to App.js for the final touches.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
The highlighted code is newly added to App.js.
First, we import our custom hook, destructure the theme and themeToggler props, and set it with the useDarkMode function.
Note that the useDarkMode method replaces our theme state, which was initially in App.js.
We declare a themeMode variable, which renders either a light or dark theme based on the condition of the theme mode at the time.
Now, our ThemeProvider wrapper component is assigned our just recently created themeMode variable to the theme prop.
And lastly, in place of the regular button, we pass in the Toggle component.
Remember that in our Toggle component, we defined and styled a button and passed both theme and toggleTheme to them as props. So, all we have to do is pass these props appropriately to the Toggle component, which will act as our button in App.js.
Yes! Our dark mode is set, and it persists, not changing color when the page is refreshed or visited in a new tab.
Let’s see the outcome in action:
Dark mode implemented, but with a glitch in the button color when the browser reloads. (Large preview)
Almost everything works well, but there is one small thing we can do to make our experience splendid. Switch to the dark theme and then reload the page. Do you see that the blue color in the button loads before the gray for a brief moment? That happens because our useState hook initiates the light theme initially. After that, useEffect runs, checks localStorage, and only then sets the theme to dark. Let’s jump over to our custom hook useDarkMode.js and add a little code:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const [mountedComponent, setMountedComponent] = useState(false) const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light') setMountedComponent(true) }, []); return [theme, themeToggler, mountedComponent] };
The highlighted code is the only one added to useDarkMode.js. We’ve created another state named mountedComponent and set the default value to false using the useState hook. Next, inside the useEffect hook, we set the mountedComponent state to true using setMountedComponent. Lastly, in the return array, we include the mountedComponent state.
Finally, let’s add a bit of code in App.js to make it all work.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, themeToggler, mountedComponent] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); if(!mountedComponent) return <div/> return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
We’ve added our mountedComponent state as a prop in our useDarkMode hook, and we’ve checked whether our component has mounted, because this is what happens in the useEffect hook. If it hasn’t happened yet, then we will render an empty div.
Let’s see the outcome of our dark-mode web page.
Final result of dark mode (Large preview)
Now, you’ll notice that while in dark mode, when the page reloads, the button’s color doesn’t change.
Conclusion
Dark mode is increasingly becoming a user preference, and implementing it in a React web app is a lot easier when using the ThemeProvider theming wrapper in styled-components. Go ahead and experiment with styled-components as you implement dark mode; you could add icons instead of a button.
Please do share your feedback and experience with the theming feature in styled-components in the comments section below. I’d love to see what you come up with!
The supporting repository for this article is available on GitHub. Also, check it out on CodeSandbox.
References
(ks, ra, il, al)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/implementing-dark-mode-in-react-apps-using-styled-components/
0 notes
Text
Implementing Dark Mode In React Apps Using styled-components
About The Author
Blessing Krofegha is a Software Engineer Based in Lagos Nigeria, with a burning desire to contribute to making the web awesome for all, by writing and building … More about Blessing …
Light mode is a convention in most web and mobile apps. However, in modern development, we have seen how dark mode, which displays light text and interface elements on a dark background, is quickly becoming a user preference. In this article, we’ll learn how to efficiently implement dark mode in a React app on a simple web page, using the styled-components library and leveraging some React features like hooks. We will also discuss the pros and cons of dark mode and why it should be adopted.
One of the most commonly requested software features is dark mode (or night mode, as others call it). We see dark mode in the apps that we use every day. From mobile to web apps, dark mode has become vital for companies that want to take care of their users’ eyes.
Dark mode is a supplemental feature that displays mostly dark surfaces in the UI. Most major companies (such as YouTube, Twitter, and Netflix) have adopted dark mode in their mobile and web apps.
While we won’t go in depth into React and styled-components, a basic knowledge of React, CSS, and styled-components would come in handy. This tutorial will benefit those who are looking to enhance their web applications by catering to those who love dark mode.
StackOverflow announces dark mode on Twitter (Large preview)
A few days before the writing of this article, StackOverflow announced its release of dark mode, giving users the chance to toggle between the two modes.
Dark mode reduces eye strain and helps when you’re working for a long time on a computer or mobile phone.
What Is Dark Mode?
Dark mode is the color scheme of any interface that displays light text and interface elements on a dark background, which makes the screen a little easier to look at mobile phones, tablets, and computers. Dark mode reduces the light emitted by the screen, while maintaining the minimum color-contrast ratios required for readability.
Why Should You Care About Dark Mode?
Dark mode enhances visual ergonomics by reducing eye strain, adjusting the screen to current light conditions, and providing ease of use at night or in dark environments.
Before implementing dark mode in our app, let’s look at its benefits.
Battery Saving
Dark mode in web and mobile apps can prolong the battery life of a device. Google has confirmed that dark mode on OLED screens has been a huge help to battery life.
For example, at 50% brightness, dark mode in the YouTube app saves about 15% more screen energy than a flat white background. At 100% screen brightness, the dark interface saves a whopping 60% of screen energy.
Dark Mode Is Beautiful
Dark mode is beautiful, and it can significantly enhance the appeal of the screen.
While most products are going for that similar bland white look, dark mode offers something different that feels mysterious and new.
It also provides great opportunities to present graphic content such as dashboards, pictures, and photos in a fresh way.
The beauty of Twitter’s dark mode over light mode (Large preview)
Now that you know why you should implement dark mode in your next web app, let’s dive deep into styled-components, which is the defining resource of this tutorial.
Dark mode is the color scheme of any interface that displays light text and interface elements on a dark background, which makes it a little easier to look at on mobile phones, tablets, and computers.
What Are styled-components?
Throughout this article, we will be using the styled-components library very often. There have always been many ways to style a modern web app. There’s the traditional method of styling at the document level, which includes creating an index.css file and linking it to the HTML or styling inside the HTML file.
A lot has changed in the ways that web apps are styled recently, since the introduction of CSS-in-JS.
CSS-in-JS refers to a pattern in which CSS is composed using JavaScript. It utilizes tagged template literals to style components in a JavaScript file.
To learn more about CSS-in-JS, check out Anna Monus’s article on the subject.
styled-components is a CSS-in-JS library lets you use all of the features of CSS that you love, including media queries, pseudo-selectors, and nesting.
Why styled-components?
styled-components was created for the following reasons:
No class name hell Instead of you scratching your head to find a class name for an element, styled-components generates unique class names for your styles. You’ll never have to worry about misspellings or using class names that have no meaning.
Using props styled-components allow us to extend styling properties using the props parameter, commonly used in React — thus, dynamically affecting the feel of a component via the application’s state.
Supports Sass syntax Writing Sass syntax out of the box without having to set up any preprocessors or extra build tools is possible with styled-components. In your style definitions, you can use the & character to target the current component, use pseudo-selectors, and experiment with nesting.
Theming styled-components have full theming support by exporting a ThemeProvider wrapper component. This component provides a theme to all React components within itself via the Context API. In the rendering tree, all styled-components will have access to the provided theme, even when they are multiple levels deep. As we continue in this tutorial, we will look deeper into the theming features of styled-components.
To learn more advantages of styled-components, check out Kris Guzman’s article.
Implementing Dark Mode
In this article, we are going to implement dark mode on a simple YouTube-like web page.
To follow along, ensure that you clone the original repository from the starter branch.
Setting Up
Let’s install all of the dependencies in our package.json file. From the terminal, run the following command:
npm install
Upon its successful installation, run npm start. Here is what the web page looks like without dark mode implemented on it.
The web page to be used, without dark mode. (Large preview)
To install styled-components, in your terminal run npm install styled-components.
Implementation
To implement dark mode, we need to create four different components.
Theme This contains the color properties of our light and dark themes.
GlobalStyles This contains the global styles for the entire document.
Toggler This holds the button element that toggles the functionality.
useDarkMode This custom hook handles the logic behind the change of theme and the persistence of our theme in localStorage.
Theme Component
In the src folder, you’ll see components in the components folder. Create a Themes.js file, and add the following code to it.
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
Here, we’ve defined and exported lightTheme and darkTheme objects with distinct color variables. Feel free to experiment and customize the variables to suit you.
globalStyles Component
Remaining in your components folder, create a globalStyles.js file, and add the following code:
import { createGlobalStyle} from "styled-components" export const GlobalStyles = createGlobalStyle` body { background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif; transition: all 0.50s linear; } `
We’ve imported createGlobalStyle from styled-components. The createGlobalStyle method replaces the now deprecated injectGlobal method from styled-components version 3. This method generates a React component, which, when added to your component tree, will inject global styles into the document, in our case, App.js.
We defined a GlobalStyle component and assigned background and color properties to values from the theme object. Thus, every time we switch the toggle, the values will change depending on the dark theme or light theme objects that we are passing to ThemeProvider (which will be created later, as we proceed).
The transition property of 0.50s enables this change to occur a little more smoothly, so that as we toggle back and forth, we can see the changes happen.
Creating Theme-Toggling Functionality
To implement the theme-toggling functionality, we need to add only a few lines of code. In the App.js file, add the following code (note that the highlighted code is what you should add):
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') } useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return ( <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/> <div className="App"> <button onClick={themeToggler}>Switch Theme</button> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
The highlighted code is the one newly added to App.js. We’ve imported ThemeProvider from styled-components. ThemeProvider is a helper component in the styled-components library that provides theming support. This helper component injects a theme into all React component below itself via the Context API.
In the rendering tree, all styled-components will have access to the provided theme, even when they are multiple levels deep. Check out the section on “Theming”.
Next, we import the GlobalStyle wrapper from ./components/Globalstyle. Lastly, from the top, we import both the lightTheme and darkTheme objects from ./components/Themes.
In order for us to create a toggling method, we need a state that holds our theme’s initial color value. So, we create a theme state, and set the initial state to light, using the useState hook.
Now, for the toggling functionality.
The themeToggler method uses a ternary operator to check the state of the theme, and it toggles either dark or light based on the value of the condition.
ThemeProvider, a styled-components helper component, wraps everything in the return statement and injects any components below it. Remember that our GlobalStyles inject global styles into our components; hence, it’s called inside the ThemeProvider wrapper component.
Lastly, we created a button with an onClick event that assigns our themeToggler method to it.
Let’s see the outcome thus far.
Dark mode implemented without persistence (Large preview)
Our App.js file needs to be refactored; a lot of its code is not DRY. (DRY stands for “don’t repeat yourself”, a basic principle of software development aimed at reducing repetition.) All of the logic seems to be in App.js; it’s good practice to separate our logic for the sake of clarity. So, we’ll create a component that handles the toggling functionality.
Toggle Component
Still within the components folder, create a Toggler.js file, and add the following code to it:
import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle;
To keep things neat, we’ve styled our toggle button in the Toggle component, using the styled function from styled-components.
This is purely for presentation; you can style the button as you see fit.
Inside the Toggle component, we pass two props:
the theme provides the current theme (light or dark);
the toggleTheme function will be used to switch between themes.
Next, we return the Button component and assign a toggleTheme function to the onClick event.
Lastly, we use propTypes to define our types, ensuring that our theme is a string and isRequired, while our toggleTheme is func and isRequired.
Using Custom Hooks (useDarkMode)
When building an application, scalability is paramount, meaning that our business logic must be reusable, so that we can use it in many places and even in different projects.
That is why it would be great to move our toggling functionality to a separate component. For that, we would create our own custom hook.
Let’s create a new file named useDarkMode.js in the components folder, and move our logic to this file, with some tweaks. Add the following code to the file:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };
We’ve added a few things here.
setMode We use localStorage to persist between sessions in the browser. So, if a user has chosen the dark or light theme, that’s what they’ll get upon their next visit to the app or if they reload the page. Hence, this function sets our state and passes theme to localStorage.
themeToggler This function uses a ternary operator to check the state of the theme and toggles either dark or light based on the truth of the condition.
useEffect We’ve implemented the useEffect hook to check on component mounting. If the user has previously selected a theme, we will pass it to our setTheme function. In the end, we will return our theme, which contains the chosen theme and the themeToggler function to switch between modes.
I think you’ll agree that our dark-mode component looks sleek.
Let’s head over to App.js for the final touches.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
The highlighted code is newly added to App.js.
First, we import our custom hook, destructure the theme and themeToggler props, and set it with the useDarkMode function.
Note that the useDarkMode method replaces our theme state, which was initially in App.js.
We declare a themeMode variable, which renders either a light or dark theme based on the condition of the theme mode at the time.
Now, our ThemeProvider wrapper component is assigned our just recently created themeMode variable to the theme prop.
And lastly, in place of the regular button, we pass in the Toggle component.
Remember that in our Toggle component, we defined and styled a button and passed both theme and toggleTheme to them as props. So, all we have to do is pass these props appropriately to the Toggle component, which will act as our button in App.js.
Yes! Our dark mode is set, and it persists, not changing color when the page is refreshed or visited in a new tab.
Let’s see the outcome in action:
Dark mode implemented, but with a glitch in the button color when the browser reloads. (Large preview)
Almost everything works well, but there is one small thing we can do to make our experience splendid. Switch to the dark theme and then reload the page. Do you see that the blue color in the button loads before the gray for a brief moment? That happens because our useState hook initiates the light theme initially. After that, useEffect runs, checks localStorage, and only then sets the theme to dark. Let’s jump over to our custom hook useDarkMode.js and add a little code:
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const [mountedComponent, setMountedComponent] = useState(false) const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light') setMountedComponent(true) }, []); return [theme, themeToggler, mountedComponent] };
The highlighted code is the only one added to useDarkMode.js. We’ve created another state named mountedComponent and set the default value to false using the useState hook. Next, inside the useEffect hook, we set the mountedComponent state to true using setMountedComponent. Lastly, in the return array, we include the mountedComponent state.
Finally, let’s add a bit of code in App.js to make it all work.
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [theme, themeToggler, mountedComponent] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); if(!mountedComponent) return <div/> return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
We’ve added our mountedComponent state as a prop in our useDarkMode hook, and we’ve checked whether our component has mounted, because this is what happens in the useEffect hook. If it hasn’t happened yet, then we will render an empty div.
Let’s see the outcome of our dark-mode web page.
Final result of dark mode (Large preview)
Now, you’ll notice that while in dark mode, when the page reloads, the button’s color doesn’t change.
Conclusion
Dark mode is increasingly becoming a user preference, and implementing it in a React web app is a lot easier when using the ThemeProvider theming wrapper in styled-components. Go ahead and experiment with styled-components as you implement dark mode; you could add icons instead of a button.
Please do share your feedback and experience with the theming feature in styled-components in the comments section below. I’d love to see what you come up with!
The supporting repository for this article is available on GitHub. Also, check it out on CodeSandbox.
References
(ks, ra, il, al)
Website Design & SEO Delray Beach by DBL07.co
Delray Beach SEO
source http://www.scpie.org/implementing-dark-mode-in-react-apps-using-styled-components/ source https://scpie1.blogspot.com/2020/04/implementing-dark-mode-in-react-apps.html
0 notes
Text
Learn React Basics in 10 Minutes
If you want to learn the basics of React in the time it takes you to drink a cup of coffee, this post is for you.
This article aims to provide a beginner-friendly introduction to React, what it is and why we need it. It assumes you have some understanding of basic JavaScript.
We will discuss some of its basic concepts and go over what you can build with React.
We will also discuss some code but the overall goal is to gain an intuitive understanding of what React is all about so that you get comfortable with the basics.
What is React?
Developed by Facebook in 2011, React has quickly become one of the most widely used JavaScript libraries. According to HackerRank, 30% of employers look for developers who know React but only about half of the applicants actually have the required knowledge.
Clearly, React is in high demand in the job market.
So what exactly is React?
React is an efficient and flexible JavaScript library for building user interfaces (with itself written using JavaScript). It breaks down complex UIs in the form of small, isolated code called “components”. By using these components, React only concerns itself with what you see on the front page of a website.
A calculator app that can be split into React components.
Components are independent and reusable. They can either be Javascript functions or classes. Either way, they both return a piece of code that represents part of a web page.
Here’s an example of a function component that renders a <h2> element onto the page:
function Name() { return <h2>Hi, my name is Joe!</h2>; }
And here is a class component doing the same rendering:
class Person extends React.Component { render() { return <h2>Hi again from Joe!</h2>; } }
Using a class component takes slightly more effort in that you have to extend React.Component (part of the React library) while a function component is mostly plain JavaScript. However, class components provide certain critical functionalities that function components lack (see Functional vs Class-Components in React).
You may have noticed that there is a strange mixture of HTML and JavaScript inside each component. React actually uses a language called JSX that allows HTML to be mixed with Javascript.
Not only can you use JSX to return pre-defined HTML elements, you can also create your own. For example, instead of rendering <h2> elements directly in the class component, you can render the functional component which returns the same thing:
class Person extends React.Component { render() { return <Name />; } }
Note the self-closing ‘/>’ of the component.
The power of React starts to become more evident as you can imagine rendering many simple components to form a more complex one.
To build a page, we can call these components in a certain order, use the results they return, and display them to the user.
Why Choose React Over Vanilla JavaScript?
Being able to break down complex UIs through the use of components obviously gives React an edge over vanilla JavaScript (plain JS without any external libraries or frameworks), but what else can React do that places it in such high demand among employers?
Let’s take a look at the differences between how React and vanilla JS handles things.
In the previous section, we discussed how React uses components to render UIs. We did not delve into what was happening on the HTML side of things. It may be surprising to learn that the HTML code that pairs with React is really simple:
<div id="root"></div>
It is usually just a <div> element with an id that serves as a container for a React app. When React renders its components, it will look for this id to render to. The page is empty before this rendering.
Vanilla JS on the other hand defines the initial UI right in the HTML.
In addition, vanilla JS takes care of functionality while HTML takes care of displaying content (markup).
In the earlier days of the web the separation of functionality and markup sounded logical as apps were simpler. However, as complexity grew so do the headaches of maintaining large pieces of JS code.
JS code that updates a piece of HTML can be spread across several files, and the developers may have a hard time keeping track of where the code came from. They have to keep things straight in their heads of all the interactions between the code that resides in different files.
React sorts the code into components, where each component maintains all the code needed to both display and update the UI.
Updating the UI requires updating the DOM, or document object model (see DOM Manipulation Using JavaScript). This is where React truly shines.
If you want to access the DOM in vanilla JS, you have to first find it before it can be used. React stores the data in regular JS variables and maintains its own virtual DOM.
If you want to then update the DOM in vanilla JS, you have to locate the appropriate node then manually append or remove elements. React automatically updates the UI based on the application state, which we will discuss in more detail in the next section.
So the primary reason why we may want to use React over vanilla JS can be summarized in one word: simplicity.
With vanilla JS, it’s easy to get lost in a maze of DOM searches and updates. React forces you to break down your app into components which produces more maintainable code.
Thus, for complex apps you will definitely want to learn React.
Basic React Concepts
We have already discussed how React uses components to break down complex UIs and JSX to render those components.
In this section we will talk about some more fundamental concepts of React.
State
As mentioned previously, React updates the UI based on the application state. This state is actually stored as a property of a React class component:
class Counter extends React.Component { state = { value: 0 }; }
Suppose we have a counter and 2 buttons that either increment or decrement. The value of the counter is rendered onto the page through JSX.
The display counter value is based on the state and we change the state by clicking one of the buttons. Vanilla JS treats a button click as an event and so does React. When such an event occurs, we will call functions that either increment or decrement the counter based on the button clicked. These functions have the code that changes the component state.
Here’s an example of such a counter:
class Counter extends React.Component { state = { value: 0 }; handleIncrement= () => { this.setState(state => { value: state.value + 1 }); }; handleDecrement= () => { this.setState(state => { value: state.value - 1 }); }; render() { return ( <div> <h2>{this.state.value}</h2> <button onClick={this.handleDecrement}>Decrement</button> <button onClick={this.handleIncrement}>Increment</button> </div> ); } };
We updated the state by calling setState in each of the functions handling a button click. The counter displayed on the page will update in real time. Thus, React gets its name because it reacts to state changes.
In short, React automatically monitors every component state for changes and updates the DOM appropriately.
Props
We can use props (short for "properties") to allow components to talk to each other.
Suppose the counter in our previous example represents the quantity of a product a customer wishes to purchase. The store wants to place a limit of 2 products purchased per customer. At checkout, we want to display an appropriate message if the customer tries to purchase more than 2.
Here’s how we may do it with props:
const Display = (props) => { let message; if(props.number>2){ message = ‘You’re limited to purchasing 2 max!’; }else{ message = ‘All’s good.’; } return( <p>message</p> ); }; class Timer extends React.Component { state = { quantity: 0 } //...code for handling button clicking, updating state, etc. render(){ return( <Display number = {this.state.quantity} /> //...code for other components ); } };
We create a functional component called Display and pass props as a parameter. When we render this component, we pass to it number as an attribute set to the quantity of the product a customer wants to purchase. This is similar to setting an attribute of an HTML tag. We call this value with props.number in Display to determine what message to return.
Component Lifecycle
As React updates the DOM based on component states, special methods called lifecycle methods exist to provide opportunities to perform actions at specific points in the lifecycle of a component.
They allow you to catch components at a certain point in time to call appropriate functions. These points of time can be before components are rendered, after they are updated, etc. You may want to explore a component’s lifecycle methods.
To see lifecycle methods in action, you can check out this Pomodoro Clock I made.
The clock timer is initially set to the session length. When the session timer counts down to zero, the timer needs to switch to the break length and start counting down from there.
Since the timer is a component, I used the lifecycle method componentDidUpdate within my main class component to handle any changes with handleChange():
componentDidUpdate() { this.handleChange(); }
You can think of lifecycle methods as adding event listeners in vanilla JS to a React component.
What Can You Build with React?
So now you have a basic understanding of React, what can you build with it?
We already mentioned in the beginning of this post that Facebook developed React in 2011, so naturally the Facebook platform is based on React. Other famous apps that either completely or partially use React include Instagram, Netflix, and Whatsapp.
But as beginners of React, we are not looking to immediately build the next Facebook so here’s a list of 10 React Starter Project Ideas to Get You Coding.
If you want to learn more about web development and check out some examples of beginner-friendly React projects, visit my blog at 1000 Mile World.
Thanks for reading and happy coding!
via freeCodeCamp.org https://ift.tt/2UicncL
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
Text
React Data Layer - Part 4: Backend Data
This post is the fourth part of an 8-part series going in-depth into how to build a robust real-world frontend app data layer. See the previous parts here:
Part 1: Intro
Part 2: Setting up React and Redux
Part 3: Login
In this post we’ll switch from storing our data only locally in-memory to reading it from the web service and writing it back to there. This step moves our app to the point where we could actually use it for production. We’ll look into patterns we can use to organize our web service requests, status reporting, and data returned.
If you like, you can download the app as of the end of the post.
Setting Up Thunk
To connect to a web service, we need a mechanism to handle our asynchronous web requests. For our purposes, the Redux Thunk library will do nicely. It allows you to run asynchronous code in your Redux action creators, dispatching actions after they complete. Install it:
$ yarn add redux-thunk
Next, add it to your store setup in src/store/index.js:
-import { createStore } from 'redux'; +import { createStore, applyMiddleware, compose } from 'redux'; import { devToolsEnhancer } from 'redux-devtools-extension'; +import thunk from 'redux-thunk'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import rootReducer from './reducers'; ... const store = createStore( persistedReducer, - devToolsEnhancer(), + compose( + applyMiddleware(thunk), + devToolsEnhancer(), + ), );
Instead of being able to pass the devToolsEnhancer() directly, we now need to apply the thunk middleware as well, so we use the Redux compose() method to compose the two enhancers into a single enhancer to pass.
Loading Data
Now it’s time for us to load data from the service.
First, let’s add an action creator to do so. Add the following to store/games/actions.js:
+import api from '../api'; + +export const STORE_GAMES = 'STORE_GAMES'; export const ADD_GAME = 'ADD_GAME'; +export const loadGames = () => async (dispatch) => { + const { data: responseBody } = await api.get('/games'); + dispatch({ + type: STORE_GAMES, + games: responseBody.data, + }); +}; + export const addGame = (title) => { return {
When using Redux Thunk, action creator functions return another function, which takes a dispatch parameter that can be used to dispatch actions. We use arrow function syntax so we can concisely show a function returning another function. We are also using ECMAScript async/await syntax to simplify the asynchronous network call. We send a GET request to the /games endpoint. We destructure the response object, assigning the data property to a responseBody variable. You can see examples of JSON:API response formats at https://sandboxapi.bignerdranch.com/; here’s an example of a response to GET /games:
{ "data": [ { "id": "1", "type": "games", "links": { "self": "https://sandboxapi.bignerdranch.com/games/1" }, "attributes": { "title": "Final Fantasy 7", } } ] }
Next, we dispatch a new STORE_GAMES action, and we pass it the data property of responseBody. Wondering why there are two nested data properties? The first is defined by Axios on its response object to make the response body available, and the second is a field within the response body defined by the JSON:API specification. You can see that "data" field in the sample response above.
Next let’s handle the STORE_GAMES action in our reducer. Add the following to store/games/reducers.js:
import { ADD_GAME, + STORE_GAMES, } from './actions'; -const initialData = [ - 'Fallout 3', - 'Final Fantasy 7', -]; - -export function games(state = initialData, action) { +export function games(state = [], action) { switch (action.type) { + case STORE_GAMES: + return action.games; case ADD_GAME: return [action.title, ...state];
Because the server is now providing our data, we no longer need the initialData, so we remove it, setting the reducer’s initial data to an empty array. When STORE_GAMES is dispatched, we replace the games reducer’s state with the games property passed in the action.
Now that our loadGames action creator is done, let’s wire it up to our UI. First, in GameList/index.js, add it to mapDispatchToProps:
import { + loadGames, addGame, } from 'store/games/actions'; ... const mapDispatchToProps = { + loadGames, addGame, logOut, };
This will make loadGames available to our GameList component as a prop. Now we can call it when the GameList mounts:
-import React from 'react'; +import React, { useEffect } from 'react'; import { Button, Collection, ... const GameList = ({ games, + loadGames, addGame, logOut, }) => { + useEffect(() => { + loadGames(); + }, []); + return <div> <AddGameForm onAddGame={addGame} /> <Button onClick={logOut}>
useEffect will dispatch our loadGames action when the component mounts. We pass an empty array to useEffect to let it know that there are no state items that should cause the effect to be rerun, so it will only run once, when the component mounts.
We need to make one more change to GameList as well. Previously, when we were only working with local data, we stored the titles of the games directly in the reducer. Now, though, entire JSON:API records are being stored. Here’s an example record again:
{ "id": "1", "type": "games", "links": { "self": "https://sandboxapi.bignerdranch.com/games/1" }, "attributes": { "title": "Final Fantasy 7", } }
We need to update our render method to take account for this new format:
<Collection> { games.map((game) => ( - <CollectionItem key={game}>{game}</CollectionItem> + <CollectionItem key={game.id}>{game.attributes.title}</CollectionItem> )) } </Collection>
Now that we have a real ID field we can use that for the key prop instead of the name. This will prevent collisions as long as the server returns a unique ID for each record. To display the game’s title, we retrieve it from the attributes.title property.
If you run the app now, you’ll likely see this error:
Unhandled Rejection (TypeError): Cannot read property 'title' of undefined
This is because we’re restoring our Redux state from where it was persisted, and the games we have from before don’t have an attributes property: they’re just strings. This illustrates one of the challenges of persisting data: you have to be aware that past users will have data in previous formats, so you will need to manually migrate it.
In our case, though, we aren’t in production, so we can just clear out our persisted state. Open the Chrome web developer tools, go to Application > Storage > Local Storage > http://localhost:3000, and click the circle with a line through it. This will remove your persisted state.
Reload and you’ll need to log back in again, but after you do, records should be pulled down from the server successfully. You should now see some sample records returned from the server; these were set up for you when you created your account.
Saving Data
Now let’s set up an action creator to add a record. To do this, we need to change our addGame action creator from synchronous to asynchronous. Replace addGame with the following:
export const addGame = (title) => async (dispatch) => { const game = { type: 'games', attributes: { title }, }; const { data: responseBody } = await api.post('/games', { data: game }); dispatch({ type: ADD_GAME, game: responseBody.data, }); };
First, we construct a game object in the format JSON:API requires. We add a type property to indicate that it’s a game. Then we include an attributes object, which for us is just the title. We POST it to the /games endpoint. As with the loading endpoint, we destructure the data property into the responseBody variable, then retrieve the data property from it. This will be the complete record returned by the server, including the id it assigned. This complete record is what we pass along with the ADD_GAME dispatch.
We also need to make a tiny change to games/reducers.js. Before, we were passing only a title property with ADD_GAME, but now we are passing an entire game. We update the games reducer to retrieve the correct property:
case STORE_GAMES: return action.games; case ADD_GAME: - return [action.title, ...state]; + return [action.game, ...state]; default: return state;
This change helps make it clear that we are now storing an entire game, rather than just the title.
With this, our data should now save to the server. Reload the app and add a new record. Then reload the page again. The record you added should still appear.
Reloading
Another nicety we could add is a “Reload” button. Say a user is logged into our app on multiple devices. If they add a game on one device, it won’t show up on the other device. Let’s add a reload button to re-request the data from the server.
Implementing this is very simple: in addition to calling the loadGames action creator in componentDidMount, we also need to bind it to a button. Make the following change in GameList.js:
return <div> <AddGameForm onAddGame={addGame} /> + <Button onClick={loadGames}> + Reload + </Button> <Button onClick={logOut}> Log Out </Button>
Try it out; you will probably not see any difference in the UI, but check your Network tab to see that another request is sent.
Loading and Error States
Our app can now read and write data to the server–now let’s think about ways we can improve it. It’d be nice if we could indicate to the user if content was being loaded, as well as if there was an error. To do this, we need to track loading and error states in our store.
First, let’s create actions to record these status changes. Add the following to store/games/actions.js:
export const STORE_GAMES = 'STORE_GAMES'; export const ADD_GAME = 'ADD_GAME'; +export const START_LOADING = 'START_LOADING'; +export const RECORD_ERROR = 'RECORD_ERROR'; export const loadGames = () => async (dispatch) => { - const { data: responseBody } = await api.get('/games'); - dispatch({ - type: STORE_GAMES, - games: responseBody.data, - }); + dispatch({ type: START_LOADING }); + try { + const { data: responseBody } = await api.get('/games'); + dispatch({ + type: STORE_GAMES, + games: responseBody.data, + }); + } catch { + dispatch({ type: RECORD_ERROR }); + } };
Before we make the GET request, we dispatch the START_LOADING action. We add a try/catch block so that if the promise we’re awaiting rejects, we will catch the error. If there’s an error, we dispatch the RECORD_ERROR action.
Now we need to handle these actions in games/reducer.js. First, import our new actions:
import { ADD_GAME, STORE_GAMES, + START_LOADING, + RECORD_ERROR, } from './actions';
Next, let’s create a new reducer for each of the loading and error flags. First, loading:
export function loading(state = false, action) { switch (action.type) { case START_LOADING: return true; case STORE_GAMES: case RECORD_ERROR: return false; default: return state; } }
The loading flag starts as false. When we dispatch START_LOADING before the request, loading is updated to true. We then set it back to false when either the request succeeds and we STORE_GAMES, or the request fails and we RECORD_ERROR.
Next, let’s set up the error reducer:
export function error(state = false, action) { switch (action.type) { case RECORD_ERROR: return true; case START_LOADING: case STORE_GAMES: return false; default: return state; } }
The error flag starts as false. When we dispatch RECORD_ERROR upon an error, error is updated to true. If a new request starts, START_LOADING will set it back to false. We also set it to false if a request succeeds and we dispatch STORE_GAMES. We don’t strictly need to do this because START_LOADING should have set it to false already, but it can make our reducer more robust if we end up sending multiple web service requests at the same time in the future.
These new reducers illustrate some of the power of how Redux decouples actions from the code that handles them. Multiple reducers are responding to the same events. This decoupling keeps all the logic around one piece of state in one place; for example, it’s easier to see when the error flag is set and unset, to catch possible errors in our implementation.
To complete the updates to games/reducers.js, add the new reducers to the combineReducers() function call:
export default combineReducers({ games, + loading, + error, });
Now that we have loading and error flags in our store, we need to display the corresponding indicators in the UI. This is a separate and potentially reusable concern, so let’s create a LoadingIndicator component to handle this. Create a src/components/LoadingIndicator.js file and add the following:
import React from 'react'; import { Preloader } from 'react-materialize'; const LoadingIndicator = ({ loading, error, children }) => { if (loading) { return <div> <Preloader size="small" /> </div>; } else if (error) { return <p>An error occurred.</p>; } else { return <div> {children} </div>; } }; export default LoadingIndicator;
It’s a pretty straightforward component: we pass loading and error props to it, as well as some JSX children. If loading is true we display an indicator; if error, the message; otherwise, we display the children.
Now let’s set up GameList/index.js to pass these new state items to GameList:
function mapStateToProps(state) { return pick(state.games, [ 'games', + 'loading', + 'error', ]); }
And now in GameList we’ll add the LoadingIndicator component and pass these props to it:
... CollectionItem, } from 'react-materialize'; import AddGameForm from './AddGameForm'; +import LoadingIndicator from 'components/LoadingIndicator'; const GameList = ({ games, + loading, + error, loadGames, addGame, logOut, ... <Button onClick={logOut}> Log Out </Button> - <Collection header="Video Games"> - { games.map((game) => ( - <CollectionItem key={game.id}>{game.attributes.title}</CollectionItem> - )) } - </Collection> + <LoadingIndicator loading={loading} error={error}> + <Collection header="Video Games"> + { games.map((game) => ( + <CollectionItem key={game.id}>{game.attributes.title}</CollectionItem> + )) } + </Collection> + </LoadingIndicator> </div>;
We nest the existing <Collection> inside the LoadingIndicator as its children; as we saw in the implementation of LoadingIndicator, the children will only be rendered when the data is not loading or errored.
Now we should be ready to see these states in action. Reload your app, then in the Network tab find the dropdown that says “No throttling”:
Change it to the value “Fast 3G”–this will slow down the request so we can see it in the “loading” state:
Then click the Reload button you added to the app. You should see the animated preloader briefly before the list appears.
Check the “Offline” checkbox, then press the Reload button again. You should see the preloader again, then the error message.
What’s Next?
With this, our app is now fully hooked up to the backend for data reading and writing. For a lot of applications, this is all you need, so you can stop right here. But you may want to take advantage of some offline features to improve your users’ experience. In the remaining posts we’ll look into doing so, talk about the costs and risks, and evaluate when it’s worth it to do so.
React Data Layer - Part 4: Backend Data published first on https://johnellrod.weebly.com/
0 notes