Tag: Intro

An Intro to JavaScript Proxy

Have you ever been in a situation where you wish you could have some control over the values in an object or array? Maybe you wanted to prevent certain types of data or even validate the data before storing it in the object. Suppose you wanted to react to the incoming data in some way, or even the outgoing data? For example, maybe you wanted to update the DOM by displaying results or swap classes for styling changes as data changes. Ever wanted to work on a simple idea or section of page that needed some of the features of a framework, like Vue or React, but didn’t want to start up a new app?

Then JavaScript Proxy might be what you’re looking for!

A brief introduction

I’ll say up front: when it comes to front-end technologies, I’m more of a UI developer; much like described non-JavaScript-focused side of The Great Divide. I’m happy just creating nice-looking projects that are consistent in browsers and all the quirks that go with that. So when it comes to more pure JavaScript features, I tend not to go too deep.

Yet I still like to do research and I’m always looking for something to add to that list of new things to learn. Turns out JavaScript proxies are an interesting subject because just going over the basics opens up many possible ideas of how to leverage this feature. Despite that, at first glance, the code can get heavy quick. Of course, that all depends on what you need.

The concept of the proxy object has been with us for quite some time now. I could find references to it in my research going back several years. Yet it was not high on my list because it has never had support in Internet Explorer. In comparison, it has had excellent support across all the other browsers for years. This is one reason why Vue 3 isn’t compatible with Internet Explorer 11, because of the use of the proxy within the newest Vue project.

So, what is the proxy object exactly?

The Proxy object

MDN describes the Proxy object as something that:

[…] enables you to create a proxy for another object, which can intercept and redefine fundamental operations for that object.

The general idea is that you can create an object that has functionality that lets you take control of typical operations that happen while using an object. The two most common would be getting and setting values stored in the object.

const myObj = {   mykey: 'value' }  console.log(myObj.mykey); // "gets" value of the key, outputs 'value' myObj.mykey = 'updated'; // "sets" value of the key, makes it 'updated'

So, in our proxy object we would create “traps” to intercept these operations and perform whatever functionality we might wish to accomplish. There are up to thirteen of these traps available. I’m not necessarily going to cover all these traps as not all of them are necessary for my simple examples that follow. Again, this depends on what you’re needing for the particular context of what you’re trying to create. Trust me, you can go a long way with just the basics.

To expand on our example above to create a proxy, we would do something like this:

const myObj = {   mykey: 'value' }  const handler = {   get: function (target, prop) {     return target[prop];   },   set: function (target, prop, value) {     target[prop] = value;     return true;   } }  const proxy = new Proxy(myObj, handler);  console.log(proxy.mykey); // "gets" value of the key, outputs 'value' proxy.mykey = 'updated'; // "sets" value of the key, makes it 'updated'

First we start with our standard object. Then we create a handler object that holds the handler functions, often called traps. These represent the operations that can be done on a traditional object which, in this case, are the get and set that just pass things along with no changes. After that, we create our proxy using the constructor with our target object and the handler object. At that point, we can reference the proxy object in getting and setting values which will be a proxy to the original target object, myObj.

Note return true at the end of the set trap. That’s intended to inform the proxy that setting the value should be considered successful. In some situations where you wish to prevent a value being set (think of a validation error), you would return false instead. This would also cause a console error with a TypeError being outputted.

Now one thing to keep in mind with this pattern is that the original target object is still available. That means you could bypass the proxy and alter values of the object without the proxy. In my reading about using the Proxy object, I found useful patterns that can help with that.

let myObj = {   mykey: 'value' }  const handler = {   get: function (target, prop) {     return target[prop];   },   set: function (target, prop, value) {     target[prop] = value;     return true;   } }  myObj = new Proxy(myObj, handler);  console.log(myObj.mykey); // "gets" value of the key, outputs 'value' myObj.mykey = 'updated'; // "sets" value of the key, makes it 'updated' 

In this pattern, we’re using the target object as the proxy object while referencing the target object within the proxy constructor. Yeah, that happened. This works, but I found it somewhat easy to get confused over what’s happening. So let’s create the target object inside the proxy constructor instead:

const handler = {   get: function (target, prop) {     return target[prop];   },   set: function (target, prop, value) {     target[prop] = value;     return true;   } }  const proxy = new Proxy({   mykey: 'value' }, handler);  console.log(proxy.mykey); // "gets" value of the key, outputs 'value' proxy.mykey = 'updated'; // "sets" value of the key, makes it 'updated'

For that matter, we could create both the target and handler objects inside the constructor if we prefer:

const proxy = new Proxy({   mykey: 'value' }, {   get: function (target, prop) {     return target[prop];   },   set: function (target, prop, value) {     target[prop] = value;     return true;   } });  console.log(proxy.mykey); // "gets" value of the key, outputs 'value' proxy.mykey = 'updated'; // "sets" value of the key, makes it 'updated'

In fact, this is the most common pattern I use in my examples below. Thankfully, there is flexibility in how to create a proxy object. Just use whatever patterns suits you.

The following are some examples covering usage of the JavaScript Proxy from basic data validation up to updating form data with a fetch. Keep in mind these examples really do cover the basics of JavaScript Proxy; it can go deeper quick if you wish. In some cases they are just about creating regular JavaScript code doing regular JavaScript things within the proxy object. Look at them as ways to extend some common JavaScript tasks with more control over data.

A simple example for a simple question

My first example covers what I’ve always felt was a rather simplistic and strange coding interview question: reverse a string. I’ve never been a fan and never ask it when conducting an interview. Being someone that likes to go against the grain in this kind of thing, I played with outside-the-box solutions. You know, just to throw it out there sometimes for fun and one of these solutions is a good bit of front end fun. It also makes for a simple example showing a proxy in use.

If you type into the input you will see whatever is typed is printed out below, but reversed. Obviously, any of the many ways to reverse a string could be used here. Yet, let’s go over my strange way to do the reversal.

const reverse = new Proxy(   {     value: ''   },   {     set: function (target, prop, value) {       target[prop] = value;              document.querySelectorAll('[data-reverse]').forEach(item => {         let el = document.createElement('div');         el.innerHTML = '‮' + value;         item.innerText = el.innerHTML;       });              return true;     }   } )  document.querySelector('input').addEventListener('input', e => {   reverse.value = e.target.value; });

First, we create our new proxy and the target object is a single key value that holds whatever is typed into the input. The get trap isn’t there since we would just need a simple pass-through as we don’t have any real functionality tied to it. There’s no need to do anything in that case. We’ll get to that later.

For the set trap we do have a small bit of functionality to perform. There is still a simple pass-through where the value is set to the value key in the target object like normal. Then there is a querySelectorAll that finds all elements with a data-reverse data attribute on the page. This allows us to target multiple elements on the page and update them all in one go. This gives us our framework-like binding action that everybody likes to see. This could also be updated to target inputs to allow for a proper two-way binding type of situation.

This is where my little fun oddball way of reversing a string kicks in. A div is created in memory and then the innerHTML of the element is updated with a string. The first part of the string uses a special Unicode decimal code that actually reverses everything after, making it right-to-left. The innerText of the actual element on the page is then given the innerHTML of the div in memory. This runs each time something is entered into the input; therefore, all elements with the data-reverse attribute is updated.

Lastly, we set up an event listener on the input that sets the value key in our target object by the input’s value that is the target of the event.

In the end, a very simple example of performing a side effect on the page’s DOM through setting a value to the object.

Live-formatting an input value

A common UI pattern is to format the value of an input into a more exact sequence than just a string of letters and numbers. An example of this is an telephone input. Sometimes it just looks and feels better if the phone number being typed actually looks like a phone number. The trick though is that, when we format the input’s value, we probably still want an unformatted version of the data.

This is an easy task for a JavaScript Proxy.

As you type numbers into the input, they’re formatted into a standard U.S. phone number (e.g. (123) 456-7890). Notice, too, that the phone number is displayed in plain text underneath the input just like the reverse string example above. The button outputs both the formatted and unformatted versions of the data to the console.

So here’s the code for the proxy:

const phone = new Proxy(   {     _clean: '',     number: '',     get clean() {       return this._clean;     }   },   {     get: function (target, prop) {       if (!prop.startsWith('_')) {         return target[prop];       } else {         return 'entry not found!'       }     },     set: function (target, prop, value) {       if (!prop.startsWith('_')) {         target._clean = value.replace(/\D/g, '').substring(0, 10);          const sections = {           area: target._clean.substring(0, 3),           prefix: target._clean.substring(3, 6),           line: target._clean.substring(6, 10)         }          target.number =            target._clean.length > 6 ? `($  {sections.area}) $  {sections.prefix}-$  {sections.line}` :           target._clean.length > 3 ? `($  {sections.area}) $  {sections.prefix}` :           target._clean.length > 0 ? `($  {sections.area}` : '';          document.querySelectorAll('[data-phone_number]').forEach(item => {           if (item.tagName === 'INPUT') {             item.value = target.number;           } else {             item.innerText = target.number;           }         });          return true;       } else {         return false;       }     }   } );

There’s more code in this example, so let’s break it down. The first part is the target object that we are initializing inside the proxy itself. It has three things happening.

{   _clean: '',   number: '',   get clean() {     return this._clean;   } },

The first key, _clean, is our variable that holds the unformatted version of our data. It starts with the underscore with a traditional variable naming pattern of considering it “private.” We would like to make this unavailable under normal circumstances. There will be more to this as we go.

The second key, number, simply holds the formatted phone number value.

The third "key" is a get function using the name clean. This returns the value of our private _clean variable. In this case, we’re simply returning the value, but this provides the opportunity to do other things with it if we wish. This is like a proxy getter for the get function of the proxy. It seems strange but it makes for an easy way to control our data. Depending on your specific needs, this might be a rather simplistic way to handle this situation. It works for our simple example here but there could be other steps to take.

Now for the get trap of the proxy.

get: function (target, prop) {   if (!prop.startsWith('_')) {     return target[prop];   } else {     return 'entry not found!'   } },

First, we check for the incoming prop, or object key, to determine if it does not start with an underscore. If it does not start with an underscore, we simply return it. If it does, then we return a string saying the entry was not found. This type of negative return could be handled different ways depending on what is needed. Return a string, return an error, or run code with different side effects. It all depends on the situation.

One thing to note in my example is that I’m not handling other proxy traps that may come into play with what would be considered a private variable in the proxy. For a more complete protection of this data, you would have to consider other traps, such as [defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/defineProperty), deleteProperty, or ownKeys — typically anything about manipulating or referring to object keys. Whether you go this far could depend on who would be making use of the proxy. If it’s for you, then you know how you are using the proxy. But if it’s someone else, you may want to consider locking things down as much as possible.

Now for where most of the magic happens for this example — the set trap:

set: function (target, prop, value) {   if (!prop.startsWith('_')) {     target._clean = value.replace(/\D/g, '').substring(0, 10);      const sections = {       area: target._clean.substring(0, 3),       prefix: target._clean.substring(3, 6),       line: target._clean.substring(6, 10)     }      target.number =        target._clean.length > 6 ? `($  {sections.area}) $  {sections.prefix}-$  {sections.line}` :       target._clean.length > 3 ? `($  {sections.area}) $  {sections.prefix}` :       target._clean.length > 0 ? `($  {sections.area}` : '';      document.querySelectorAll('[data-phone_number]').forEach(item => {       if (item.tagName === 'INPUT') {         item.value = target.number;       } else {         item.innerText = target.number;       }     });      return true;   } else {     return false;   } }

First, the same check against the private variable we have in the proxy. I don’t really test for other types of props, but you might consider doing that here. I’m assuming only that the number key in the proxy target object will be adjusted.

The incoming value, the input’s value, is stripped of everything but number characters and saved to the _clean key. This value is then used throughout to rebuild into the formatted value. Basically, every time you type, the entire string is being rebuilt into the expected format, live. The substring method keeps the number locked down to ten digits.

Then a sections object is created to hold the different sections of our phone number based on the breakdown of a U.S. phone number. As the _clean variable increases in length, we update number to a formatting pattern we wish to see at that point in time.

A querySelectorAll is looking for any element that has the data-phone_number data attribute and run them through a forEach loop. If the element is an input where the value is updated, the innerText of anything else is updated. This is how the text appears underneath the input. If we were to place another input element with that data attribute, we would see its value updated in real time. This is a way to create one-way or two-way binding, depending on the requirements.

In the end, true is returned to let the proxy know everything went well. If the incoming prop, or key, starts with an underscore, then false is returned instead.

Finally, the event listeners that makes this work:

document.querySelectorAll('input[data-phone_number]').forEach(item => {   item.addEventListener('input', (e) => {     phone.number = e.target.value;   }); });  document.querySelector('#get_data').addEventListener('click', (e) => {   console.log(phone.number); // (123) 456-7890   console.log(phone.clean); // 1234567890 });

The first set finds all the inputs with our specific data attribute and adds an event listener to them. For each input event, the proxy’s number key value is updated with the current input’s value. Since we’re formatting the value of the input that gets sent along each time, we strip out any characters that are not numbers.

The second set finds the button that outputs both sets of data, as requested, to the console. This shows how we could write code that requests the data that is needed at any time. Hopefully it is clear that phone.clean is referring to our get proxy function that’s in the target object that returns the _clean variable in the object. Notice that it isn’t invoked as a function, like phone.clean(), since it behaves as a get proxy in our proxy.

Storing numbers in an array

Instead of an object you could use an array as the target “object” in the proxy. Since it would be an array there are some things to consider. Features of an array such as push() would be treated certain ways in the setter trap of the proxy. Plus, creating a custom function inside the target object concept doesn’t really work in this case. Yet, there are some useful things to be done with having an array as the target.

Sure, storing numbers in an array isn’t a new thing. Obviously. Yet I’m going to attach a few rules to this number-storing array, such as no repeating values and allowing only numbers. I’ll also provide some outputting options, such sort, sum, average, and clearing the values. Then update a small user interface that controls it all.

Here’s the proxy object:

const numbers = new Proxy([],   {     get: function (target, prop) {       message.classList.remove('error');        if (prop === 'sort') return [...target].sort((a, b) => a - b);       if (prop === 'sum') return [...target].reduce((a, b) => a + b);       if (prop === 'average') return [...target].reduce((a, b) => a + b) / target.length;        if (prop === 'clear') {         message.innerText = `$  {target.length} number$  {target.length === 1 ? '' : 's'} cleared!`;         target.splice(0, target.length);         collection.innerText = target;       }        return target[prop];     },     set: function (target, prop, value) {       if (prop === 'length') return true;        dataInput.value = '';       message.classList.remove('error');        if (!Number.isInteger(value)) {         console.error('Data provided is not a number!');         message.innerText = 'Data provided is not a number!';         message.classList.add('error');         return false;       }        if (target.includes(value)) {         console.error(`Number $  {value} has already been submitted!`);         message.innerText = `Number $  {value} has already been submitted!`;         message.classList.add('error');         return false;       }        target[prop] = value;       collection.innerText = target;       message.innerText = `Number $  {value} added!`;        return true;   } });

With this example, I’ll start with the setter trap.

First thing to do is to check against the length property being set to the array. It just returns true so that it would happen the normal way. It could always have code in place in case reacting to the length being set if we needed.

The next two lines of code refer to two HTML elements on the page stored with a querySelector. The dataInput is the input element and we wish to clear it on every entry. The message is the element that holds responses to changes to the array. Since it has the concept of an error state, we make sure it is not in that state on every entry.

The first if checks to see if the entry is in fact a number. If it is not, then it does several things. It emits a console error stating the problem. The message element gets the same statement. Then the message is placed into an error state via a CSS class. Finally, it returns false which also causes the proxy to emit its own error to the console.

The second if checks to see if the entry already exists within the array; remember we do not want repeats. If there is a repeat, then the same messaging happens as in the first if. The messaging is a bit different as it’s a template literal so we can see the repeated value.

The last section assumes everything has gone well and things can proceed. The value is set as usual and then we update the collection list. The collection is referring to another element on the page that shows us the current collection of numbers in the array. Again, the message is updated with the entry that was added. Finally, we return true to let the proxy know all is well.

Now, the get trap is a bit different than the previous examples.

get: function (target, prop) {   message.classList.remove('error');    if (prop === 'sort') return [...target].sort((a, b) => a - b);   if (prop === 'sum') return [...target].reduce((a, b) => a + b);   if (prop === 'average') return [...target].reduce((a, b) => a + b) / target.length;    if (prop === 'clear') {     message.innerText = `$  {target.length} number$  {target.length === 1 ? '' : 's'} cleared!`;     target.splice(0, target.length);     collection.innerText = target;   }    return target[prop]; },

What’s going on here is taking advantage of a “prop” that’s not a normal array method; it gets passed along to the get trap as the prop. Take for instance the first “prop” is triggered by this event listener:

dataSort.addEventListener('click', () => {   message.innerText = numbers.sort; });

So when the sort button is clicked, the message element’s innerText is updated with whatever numbers.sort returns. It acts as a getter that the proxy intercepts and returns something other than typical array-related results.

After removing the potential error state of the message element, we then figure out if something other than a standard array get operation is expected to happen. Each one returns a manipulation of the original array data without altering the original array. This is done by using the spread operator on the target to create a new array and then standard array methods are used. Each name should suggest what it does: sort, sum, average, and clear. Well, OK, clear isn’t exactly a standard array method, but it sounds good. Since the entries can be in any order, we can have it give us the sorted list or do math functions on the entries. Clearing simply wipes out the array as you might expect.

Here are the other event listeners used for the buttons:

dataForm.addEventListener('submit', (e) => {   e.preventDefault();   numbers.push(Number.parseInt(dataInput.value)); });  dataSubmit.addEventListener('click', () => {   numbers.push(Number.parseInt(dataInput.value)); });  dataSort.addEventListener('click', () => {   message.innerText = numbers.sort; });  dataSum.addEventListener('click', () => {   message.innerText = numbers.sum; });  dataAverage.addEventListener('click', () => {   message.innerText = numbers.average; });  dataClear.addEventListener('click', () => {   numbers.clear; });

There are many ways we could extend and add features to an array. I’ve seen examples of an array that allows selecting an entry with a negative index that counts from the end. Finding an entry in an array of objects based on a property value within an object. Have a message returned on trying to get a nonexistent value within the array instead of undefined. There are lots of ideas that can be leveraged and explored with a proxy on an array.

Interactive address form

An address form is a fairly standard thing to have on a web page. Let’s add a bit of interactivity to it for fun (and non-standard) confirmation. It can also act as a data collection of the values of the form within a single object that can be requested on demand.

Here’s the proxy object:

const model = new Proxy(   {     name: '',     address1: '',     address2: '',     city: '',     state: '',     zip: '',     getData() {       return {         name: this.name || 'no entry!',         address1: this.address1 || 'no entry!',         address2: this.address2 || 'no entry!',         city: this.city || 'no entry!',         state: this.state || 'no entry!',         zip: this.zip || 'no entry!'       };     }   },   {     get: function (target, prop) {       return target[prop];     },     set: function (target, prop, value) {       target[prop] = value;              if (prop === 'zip' && value.length === 5) {         fetch(`https://api.zippopotam.us/us/$  {value}`)           .then(response => response.json())           .then(data => {             model.city = data.places[0]['place name'];             document.querySelector('[data-model="city"]').value = target.city;                        model.state = data.places[0]['state abbreviation'];             document.querySelector('[data-model="state"]').value = target.state;           });       }              document.querySelectorAll(`[data-model="$  {prop}"]`).forEach(item => {         if (item.tagName === 'INPUT' || item.tagName === 'SELECT') {           item.value = value;         } else {           item.innerText = value;         }       })              return true;     }   } );

The target object is quite simple; the entries for each input in the form. The getData function will return the object but if a property has an empty string for a value it will change to “no entry!” This is optional but the function gives a cleaner object than what we would get by just getting the state of the proxy object.

The getter function simply passes things along as usual. You could probably do without that, but I like to include it for completeness.

The setter function sets the value to the prop. The if, however, checks to see if the prop being set happens to be the zip code. If it is, then we check to see if the length of the value is five. When the evaluation is true, we perform a fetch that hits an address finder API using the zip code. Any values that are returned are inserted into the object properties, the city input, and selects the state in the select element. This an example of a handy shortcut to let people skip having to type those values. The values can be changed manually, if needed.

For the next section, let’s look at an example of an input element:

<input class="in__input" id="name" data-model="name" placeholder="name" />

The proxy has a querySelectorAll that looks for any elements that have a matching data attribute. This is the same as the reverse string example we saw earlier. If it finds a match, it updates either the input’s value or element’s innerText. This is how the rotated card is updated in real-time to show what the completed address will look like.

One thing to note is the data-model attribute on the inputs. The value of that data attribute actually informs the proxy what key to latch onto during its operations. The proxy finds the elements involved based on that key involves. The event listener does much the same by letting the proxy know which key is in play. Here’s what that looks like:

document.querySelector('main').addEventListener('input', (e) => {   model[e.target.dataset.model] = e.target.value; });

So, all the inputs within the main element are targeted and, when the input event is fired, the proxy is updated. The value of the data-model attribute is used to determine what key to target in the proxy. In effect, we have a model-like system in play. Think of ways such a thing could be leveraged even further.

As for the “get data” button? It’s a simple console log of the getData function…

getDataBtn.addEventListener('click', () => {   console.log(model.getData()); });

This was a fun example to build and use to explore the concept. This is the kind of example that gets me thinking about what I could build with the JavaScript Proxy. Sometimes, you just want a small widget that has some data collection/protection and ability to manipulate the DOM just by interacting with data. Yes, you could go with Vue or React, but sometimes even they can be too much for such a simple thing.

That’s all, for now

“For now” meaning that could depend on each of you and whether you’ll dig a bit deeper into the JavaScript Proxy. Like I said at the beginning of this article, I only cover the basics of this feature. There is a great deal more it can offer and it can go bigger than the examples I’ve provided. In some cases it could provide the basis of a small helper for a niche solution. It’s obvious that the examples could easily be created with basic functions doing much the same functionality. Even most of my example code is regular JavaScript mixed with the proxy object.

The point though is to offer examples of using the proxy to show how one could react to interactions to data — even control how to react to those interactions to protect data, validate data, manipulate the DOM, and fetch new data — all based on someone trying to save or get the data. In the long run, this can be very powerful and allow for simple apps that may not warrant a larger library or framework.

So, if you’re a front-end developer that focuses more on the UI side of things, like myself, you can explore a bit of the basics to see if there are smaller projects that could benefit from JavaScript Proxy. If you’re more of a JavaScript developer, then you can start digging deeper into the proxy for larger projects. Maybe a new framework or library?

Just a thought…

The post An Intro to JavaScript Proxy appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, ,

The New CSS-Tricks Video Intro by dina Amin

You know we do video screencasts, right? It’s not, like, super regular, but I have done them for a long time, still like doing them, and plan to keep doing them. I publish them here, but you can subscribe over on YouTube as well.

I’ve had a couple of different custom video intro animations over the years, always done by someone far more handy with that kind of thing than I am. When I asked around in May this year, I got some good leads, but none better than Matthias paging Marc, and then Marc helping me out with an introduction to dina Amin.

One look at dina’s work and it’s an obvious: yes! She does stop-motion and a lot of breakin’ stuff:

Just one small example, check out the show reel too!

We chatted back and forth, scoping out the project. She says:

I worked together with Assem Kamal on a new intro for CSS-Tricks YouTube channel. We wanted to make something very short yet interesting so that audiences on YouTube don’t skip the intro or get bored if they watch a couple of CSS-Tricks videos back to back.

She researched and asked a bunch of questions. I like how she was very aware of the role an intro like this plays in a video, especially tutorials. Can’t be too long. Can’t be annoying in any way. It has to have enough detail that it’s almost fun to see multiple times.

The old video started with a keyboard:

We started with an Apple keyboard, because we wanted to keep something from the original intro that Chris’ audience would relate to, and most importantly because I wanted to take the keyboard apart!

“Did we cut up that keyboard?!” Yes, we did. It was too easy to find multiple broken Apple keyboards, it’s a very well-engineered product and it all comes together beautifully with minimum parts, but only Apple can fix this product. You can’t just get your screw kit out and open this up and fix one flawed button connection. So a lot of these keyboards are thrown away because it’s too expensive to fix in countries like Egypt. We got our hands on three keyboards and we cut up one as we animated and used different keyboard buttons from the other two to make the buttons stretch.

It was fun seeing some behind-the-scenes stuff in her Instagram Stories:

And another connection from the original is the idea of websites as components and building out layouts. That was just referenced in the original with some background sketches and now comes to life with paper prototypes in this version.

We thought of showing the ‘how to make a website’ process in very abstract steps where each step quickly transitions and transforms into the other. Starting with keyboard clicks that turn into code, then design blocks that make up a website, which scrolls to reveal the CSS-Tricks logo.

It’s all done quite literally with stop motion! Hand moving bits around, taking photos, and making those into video.

Once we got the concept approved and our props ready, we spent hours and hours moving little pieces to make all this magic.

Check out a time lapse of the creation!

Ultimately, I got a number of versions in different aspect ratios and sizes, which is wonderful as I can switch it up and use different ones that might be more appropriate in different scenarios. Here’s the main one!

I’ve already been putting these on the start and end of videos like this one.

Thanks, dina and Assam!

The post The New CSS-Tricks Video Intro by dina Amin appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Get the Complete Intro to Web Development and Intro to React (with Hooks!) with Brian Holt 🎣

(This is a sponsored post.)

Hey, Marc here from Frontend Masters — excited to support CSS-Tricks ❤️!

Have you checked out Brian Holt’s courses yet? His most popular courses are the “Complete Intro” courses which give you the lay of the land in Web Development as well as the entire React ecosystem.

Complete Intro to Web Development, v2

This Complete Intro to Web Development assumes no prior coding knowledge, taking you from not knowing how websites are made to writing code for your own sites in HTML, CSS, JavaScript…all the way to building a server with Node.js!

You’ll learn to…

  • Setup the tools you need to write code.
  • Write HTML and CSS to put content on a websites and make them look aesthetically pleasing.
  • Make websites interactive with JavaScript, the de facto programming language of the web.
  • Use Git and other modern development tools through your computer’s terminal to save your work and pull in code libraries.
  • Use JavaScript via Node.js to serve your own website from a server.

Complete Intro to React, v5

In the Complete Intro to React course, you’ll start from the ground up, getting all the way to using the latest features in React, including hooks, effects, context, and portals. Throughout the course, you’ll piece together tools from the entire React ecosystem (like Reach Router) to build a full application to browse adoptable pets.

You’ll learn to…

  • Use the new hooks and effects methods to handle state in function components.

  • Understand how React works by writing React with and without JSX.

  • Package client-side applications with Parcel.

  • Leverage Prettier and ESLint to maintain high-quality code.

  • Route to pages and search results with Reach Router.

  • Grab API data and update state asynchronously in an effect.

Intermediate React, v2

The Intermediate React, v2 course is a modular course where you can pick and choose the various pieces of the react ecosystem you want to learn.

You’ll learn to…

  • Learn all the types of hooks, including useReducer, useCallback, useRef and more.

  • Make CSS local to your JavaScript components using the Emotion library.

  • Load your apps fast using code splitting, Suspense React and server-side rendering.

  • Use TypeScript for writing scalable apps with superior developer experience.

  • Redux for state management

  • Test your React applications using Jest.

You’ll love Brian’s awesome courses!

Join Now

Direct Link to ArticlePermalink

The post Get the Complete Intro to Web Development and Intro to React (with Hooks!) with Brian Holt 🎣 appeared first on CSS-Tricks.


, , , , , , ,

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

, , , , , ,

Intro to React Hooks

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

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

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

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

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

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

Defining state with useState()

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

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

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

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

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

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

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

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

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

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

Create side effects with useEffect()

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

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

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

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

import { useEffect } from 'react';

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

import { useState, useEffect } from 'react';

Or, construct them:

const { useState, useEffect } = React;

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

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

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

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

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

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

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

OK, now let’s render our component!

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

Here’s what that gets us:

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

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

Context and useContext()

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

const CountContext = React.createContext();

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

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

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

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

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

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

Wrapping up

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

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

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


, ,