Skip to content
Author Micah Cambre

react reflections in 2020

A few years ago, realizing that both Javascript and React are probably the future of my career in web development, I slowly started reading more and casually learning what I could. I’ve come from such a front-of-the-front-end mindset, bordering on being a developer envying to be a designer, that learning Javscript has taken years for me.

Now that I have both training experience and real-world application of React, I want to jot down various things that I’ve learned as well as still struggle with. (It’ll also be entertaining to re-read this in a while to see how primitive my likes and dislikes are!)

Logical Operators, aka &&

I think this is one of my favorite ways to render components in React. It’s concise and very readable.

const [ activeElement, setActiveElement ] = useState(false);

return (
  <>
    {activeElement && <Accordion onClick={handleClick} />}
  </>
)

Is activeElement true? Sweet, let’s render that Accordion component. Easy to understand, easy to remember.

Form components

One of the more confusing parts of React is Controlled Components vs Uncontrolled Components. Most of the components I’ve written are uncontrolled because of the amount of things I have to do with them. In fact, I find it difficult to justify writing controlled components because doing this misses things for me.

<input
  className="contact-input"
  id="first-name"
  type="text"
  name="first-name"
  defaultValue={first_name}
  onClick={() => setSelectedField("first-name")}
  onKeyDown={e => handleKeyDown(e, "first-name")}
  onChange={e => setData({ ...data, first_name: e.target.value })}
  onKeyUp={validateInput}
/>

See all of these event handlers? Whether it’s validation or another key dependent event, even a custom click handler for custom action, controlled components seem too restricted in what they provide. The biggest drawback for uncontrolled components, unfortunately, is taking the power away from the form’s global handlers like onSubmit. You have to handle this event in the click handler or keydown handler to be able to send or receive the proper data for the element.

Functional Components + Hooks > Class Components

Keeping a mindset of using state in a hook, this allows a few quite appealing benefits.

  • Avoids prop drilling – Whether it’s encapsulating state with useState() or accessing it more globally with useReducer(), state doesn’t have to flow through multiple components to be manipulated as props
  • Less code – It feels and looks better to write functional components with hooks like useState().
  • What lifecycle methods? – I didn’t have to spend a lot of time with a method like ComponentDidMount (among the rest) before I started using useEffect() for most methods to work with data. The more I use it, the more I like its simplicity of helping data flow.

I much prefer updating state with useState() instead of setState(), despite doing similar things. Maybe this is just my personal optics?

Avoiding Prop Drilling

It took me some time to really get the concept of state and props, despite the seemingly simple ways to use them. One of the least favorite things I have to do is find where data starts and how it flows. When you have a microapp that declares a lot of state at top, somehow the the child components need to use a lot of that state and it could pass several levels down before it gets to the correct component for updating as state. Here’s a small example.

function Profile() {
  const [name, setName] = React.useState("")
  
  return <Form name={name} setName={setName} />
}

function Form({name, setName}) {
  const handleChange = e => {
    setName(e.target.value)
    // Do other things here...
  }

  return (
    <div class="form-component">
      <input 
        className="form-input"
        value={name} 
        onChange={() => handleChange(e)}
      />
      <button type="button" onClick={() => setName(name)}>Update
    </div>
  )
}

You get the idea even if this is imperfect. Essentially, we’re giving this form two ways to update: first through the normal onChange method which updates state, which can then be submitted normally, and second using a normal button that sets the name that was already updated by the input field. While this is fairly simple, as an app grows, such as multiple fieldsets of form fields or possibilities of what needs to be stored, updated, created, and so on, props need to be passed down to the various components that are especially written for the form.

If anything, using hooks has made my functions pretty single purposed in what they do to avoid complexity. Maybe it’s more verbose in some ways but it’s ultimately more readable.

Manipulating fetched data

JSON data is still one of my bigger challenges as I learn React and modern Javascript at broad. It can be tedious trying to figure out how to get data from an API to the component in the most efficient way.

Let’s fetch some data.

function PeopleProfiles() {
  const [ profiles, setProfiles ] = useState([])
  const [ activeProfile, setActiveProfile ] = useState(undefined)

  const fetchData = async () => {
    const results = await fetch("https://swapi.co/api/people/1/")
      .then(data => setProfiles(data))
      .catch(err => setErrors(err))
    const activePorfileExists = results.data.some(item => activeProfile === item.name)

    if (!activeProfileExists) setActiveProfile(results.data[0].name)
    setProfiles(results.data)
    return results
  }

  useEffect(() => {
    fetchData()
      .then(results => setActiveProfile(results.data[0].name))
  }, []);
}

Note: this probably won’t work but it’s an okay example for this purpose

Once the data is fetched from the API, we add the data to the profiles state using setProfiles. From there, we can replace, update, create and delete as needed. For visual purposes, there’s an active profile state that will choose the first object if no other profiles are in the active state.

All of this is performed when the component mounts inside of useEffect(). You can see that we’re only calling it once

Because we’re calling the data inside of useEffect() with an empty array as the second argument, this only runs when the component mounts the first time. Any time I tried to add state to the second argument, I kept getting a 429 error where it makes too many calls. I needed to update the state dynamically, which would be equivalent to componentDidUpdate.

I found a better way to update the view with new data is to fetch the data outside of useEffect() and call this function from the component or method that needs the view to update and show the updated data.

Let’s say, within the profile component, I have an input that allows me to update the data. In an ideal scenario, API data fetches are encapsulated into a global file. This app isn’t big enough for that so I’m passing fetchData down to the child component in order to update the view with new data.

  return (
    <>
      <form
        className="Form Form--LovedOnesSpecialDates"
        id="editLovedOne"
      >
        <div className="Form--LovedOnesSpecialDates__wrapper">
          {Profiles.map(profile =>
            <Profiles
              key={profile.id}
              fetchData={fetchData}
            />  
          )}
        </div>
      </form>
    </>
  );

I think this is one of the biggest problem areas with using React. Best practices for fetching and manipulating data have changed a few times as the language has updated itself. I’ve dived deep into React after ES6 was a big part of React via babel and webpack. I also didn’t have to spend a lot of time to understand React’s class component structure. There was just Javascript

But still, there’s a lot of legacy and historical knowledge of plain Javascript that is needed to really help understand the nuances of what React is doing whether it’s mapping through an array or knowing how to pass data from the API to the application’s state.

Conditional loading

Related to logical operators above, React’s conditional loading is not always obvious but interesting nonetheless.

const LovedOne = ({
  id, // number
  activeProfile, // string that's actual a number
  showAddProfile // boolean
}) => {
  return (
    <article
      className={`Profile${parseInt(activeProfile) === id ? " profile-active" : ""}`}
      onKeyDown={!showAddProfile ? handleProfileSwitcher : undefined}
      onClick={!showAddProfile ? handleProfileSwitcher : undefined}
    >
      // Add code here
    </article>
  )
}

It took me too long to find these patterns for render props.

For some reason, I could not pass down a number in the render prop called activeProfile. The console kept complaining so I had to turn the number into a string to get it into this component. So how do I actually use it?

The first attribute is the using a ternary function to set a specific class. The part that wasn’t obvious was matching the id to the activeProfile, which weren’t matching since it’s a string and number. I guess a using loose equality with a == might have solved it, but some linters don’t like it in some situations so I always default to strict equality ===. Either way, I had to turn the string into a number to be able to evaluate if they equal each other.

The second pattern was also a ternary that uses undefined to prevent onKeyDown or onClick from rendering in the markup. This was necessary because at larger viewport widths, these article elements don’t need to be used to help switch the active profile.

The rest

I think I’ll never stop cringing at PascalCase component names both in markup and stylesheets or using className instead of class among other things. I’m still caught in between writing classical components, HTML, CSS and JS vs how React based components. Let’s see what another year of this does!

Good riddance, #chrome. Hello #brave. But #firefox forever. I can’t find a reason to install Chrome on any new computer anymore and have removed it from my existing devices. #privacy

night time, city lights in the background, at night, showing a telephone post with text saying 'big data is watching you'

online resolutions

I never made formal resolutions for any new year. That said, it’s a good practice to better yourself so I think I’ll continue what I started doing last year with my digital presence.

I’m struggling to find a balance between personal privacy and being public. I don’t know where to draw the line right now. For that reason, I’ve practically given up publishing my content or data online. It’s gloomy to feel like the internet wants to do nothing more than take advantage of me. That’s where most of my thoughts go every time I want to post something like this.

So, as a continuation of a personal resolution to take back control of my digital life instead of mindlessly feeding my data into all the apps, I will do two things this year.

Hide

I began a personal quest to protect myself from services like Facebook by encapsulating how I use it everywhere. In a significant way, I’ve removed my voice from most apps and sites by participating as little as possible. I’ve removed any data I can from search engines that I don’t think belongs there. I continue doing these things like not installing Google Chrome on any computer and uninstall it from all my personal computers. Chrome is replaced with Brave Browser but I primarily use Firefox for everything now. I will continue to wind down using large companies products as I’m able to and support freely available open source software.

This all comes at a cost. I don’t have a way to privately share my life outside of some targeted private messages, calls or seeing people in person. It’s extreme and uneasy to disconnect from large parts of society. However, the more I learn about what’s going on, the more I think this active approach to privacy is needed.

Share

I don’t know how I’ll find a good balance, but I must consider that the majority of people I care about live outside of my personal physical and digital bubble. I want to be more present with life outside of occasional private messages. Anything that goes here on this website will be public enough that data aggregators can have it. I can’t control that part but I can control what I put out there.

This is a public pledge that I treat as a fun adventure to learn and help society however possible. I will live as a naive optimist that privacy isn’t completely dead, doesn’t have to be, and there’s always a good way to be online. The internet has provided so much to me, but it’s now a place that takes even more from me. I choose not to give it so much. I will continue this journey by embracing ideals of using small technology.

Inspiration

This is me reminding myself that we’re living in the best of times in human history. Even if my privacy fears are real, I need reminders that ultimately I’ll be fine. Too often, nothing is as bad as it seems in a moment. I can’t change reality but I can change how I react.

20 plus 20

This year, I lived as many years after 2000 as I did before 2000.

Replied to Noteworthy Admin CSS changes in WordPress 5.3 (Make WordPress Core)

WordPress 5.3 will introduce a number of CSS changes in WordPress admin. While the necessity to improve wp-admin accessibility was previously raised in several Trac tickets, Gutenberg’s recent inte…

I admire seeing WordPress move forward keeping accessibility as a priority after its misstep in the 5.0 release. These buttons are a continuation of this priority.

However, it’s disappointing to see a block of form controls and buttons that are too close in design aesthetic to each other.

The image I attached in my previous post is what I see on the comments list page in the Dashboard. I see multiple UI and UX problems with the above block.

I could list out the problematic state changes that are hard to see to the nearly identical styles between input fields and buttons. Inconsistent component heights, confusing applied border colors, Midnight Admin Color Scheme active button state on “Search Comments” button being completely red, and more are things I would attribute to not enough QA and testing for this release.

I think this mini-audit above should give you enough alarm to resolve these issues asap, in the next point releases.

wordpress 5.3 buttons are weird

I updated WordPress to version 5.3, saw the changes to the button styles, and then came upon this.

buttons within WordPress 5.3

I like the updated button styles, which in isolation, are transparent background colors with text and borders that are colored. This style works sometimes and is a good contrast from the button styles with solid background colors.

I don’t like that the hover or focus states of these new button styles barely changes. I also don’t like how closely the buttons mirror the form elements. There’s inconsistency in button styles throughout the whole system now with some that use solid background colors and others using transparent background colors.

I’m disappointed in this from a UX perspective. The overall state of WordPress accessibility might have improved in 5.3 but this feels like a regression.

EDIT: It looks like many others share this opinion, in different ways.
https://make.wordpress.org/core/2019/10/18/noteworthy-admin-css-changes-in-wordpress-5-3/

wifi only

My phone is at home, switched into airplane mode so cellular radio is off, but connected to Wifi with calling enabled. Unnecessary sure, but interesting! Better for security? Probably not. But less is more, right?

I hope #windows will eventually work on dual monitor redraws and windows placements with a secondary monitor. It’s annoying I split windows between a laptop monitor and secondary monitor, unplug and re-plug, where the windows placements aren’t remembered.

windows dual monitor sucks

It’s that simple. What’s not so simple is the nuance and variability of getting it right. So many graphics cards, so many retina and non-retina displays and monitors, moving windows from one monitor to the other, plugging in and unplugging the second screen, it’s quite a mess.

AMD’s method or redrawing the GUI on a new or second monitor is weird, where everything starts at the top left. In fact, often the windows all get shoved to the top left in this scenario instead of a redraw in relatively the same place.

Maybe it’s just my Acer monitor? Plugging my XPS 9575 into my Acer 27″ monitor can take up to 15 seconds to get everything in its place. But then again, Windows doesn’t seem to remember the placement of the session’s previous layout with two monitors. If I have a full-screen editor in my laptop monitor with other browser windows on the secondary monitor (set as my primary), and then I unplug and later re-plug back in, that full-screen editor takes up the secondary monitor.

Moving windows between the monitors is also an annoyance. With the XPS high resolution vs the Acer’s standard resolution, the redraws that happen during moving one to the other can cause enough rendering issues that things get squashed, resized, and displaced on the destination monitor.

I can look past a lot of this but I don’t enjoy the experience of connecting two monitors together with Windows 10.

craft cms inside of laravel homestead on windows 10 using wsl

Install Craft button

I’m jotting this down for future Micah because there are a couple of things in this process that weren’t obvious to me despite at least a couple of tutorials going through the process of installing Craft CMS from scratch using Laravel Homestead on Windows 10. I wanted to include Windows Subsystem for Linux (WSL) so this goes a little further.

I will mirror a similar tutorial that has a lot of the same steps but I will highlight on my pain points because of using the Bash script.

Set Windows up

Let’s get Windows set up first. Configure and install the following below, or use the commands below this list.

Use Powershell for these commands.
Install Chocolatey

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

Install Virtualbox and Vagrant

cinst virtualbox vagrant

Install the Homestead Vagrant box

vagrant box add laravel/homestead

This will take a while, possibly more than an hour if your internet speed is low. I’d suggest you keep this running the background in another terminal window while you take care of the rest of this. If you haven’t already installed WSL below, I’d wait to run the above command until you reboot Windows.

Install WSL

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

WSL made this process a little more challenging because Bash inside of Windows is not yet a first class citizen when it comes to traversing the operating system. Within the Ubuntu distro, there are packages that need to be added.

Use Bash on WSL for these commands.

Check what’s already installed in WSL

apt list --installed

Install what you don’t have from the following.

Update packages list

sudo apt-get update

Install PHP latest version and verify it installed

sudo apt-get install php
php -v

Install PHP CURL

sudo apt-get install php-curl

This fixed an error I kept getting:

craftcms/cms [version number] requires ext-curl * -> the requested PHP extension curl is missing from your system.

Install Composer and move into global path

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/local/bin/composer

Verify everything is installed so far, now it’s time to get through the rest of the installation process.

Use Bash on WSL for the rest of this.

Homestead configuration

Note: I am approaching this as a global installation because it allows Craft to stay encapsulated from the web server that Homestead creates. It also allows you to use Homestead for multiple projects for the same Homestead environment.

Create projects directory or use existing one; this is where your web projects live.

C:\Users\[username]\Sites\

Above is an example on my OS where my web projects live. Now let’s go in there and create the Homestead container directory for your Craft projects.

cd C:\Users\[username]\Sites\
mkdir [homestead_container] && cd [homestead_container]

where [homestead_container] is the name of the company or project. This directory will contain two sub-directories, one for the Craft repository files and the other for vendor files containing Homestead.

You should now be in the following directory shown in Bash:

/mnt/c/Users/[username]/Sites/[homestead_container]

Continue using Bash on WSL for the following commands in the current directory.

Install Homestead

composer require laravel/homestead

Generate Vagrantfile and Homestead files

php vendor/bin/homestead make

Install Craft

Install Craft (for new installations)

composer create-project craftcms/craft [Path]

where [Path] is the name of the sub-directory containing Craft. I just called it craft.

Your directory structure will probably match this screenshot
project files

Configure Homestead

Open Homestead.yaml in your editor. As of Homestead 9.0.7, you’ll see the following generated code in the Homestead file.

ip: 192.168.10.10
memory: 2048
cpus: 2
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
    - ~/.ssh/id_rsa
folders:
    -
        map: ~/code
        to: /home/vagrant/code
sites:
    -
        map: craft.test
        to: /home/vagrant/code/public
databases:
    - homestead
features:
    -
        mariadb: false
    -
        ohmyzsh: false
    -
        webdriver: false
name: [homestead_container]
hostname: [homestead_container]

This is where I started with my confusion. I’ll highlight only a few lines that might cause issues.

ip: 192.168.10.10

Most of the time, this won’t be an issue. If your internal network, or LAN, is using a network in the range of ip: 192.168.10.x, you’ll have to change the last two ranges of the IP address so that it’s not starting with 10.

As long as the above is valid, the next issue to verify is that your firewall won’t prevent the localhost from starting. Sometimes firewalls can be aggressive and Windows Defender, VPNs like NordVPN, PIA, Mullvad and others, or Antivirus applications can all prevent localhost connections from being made. Each has its own IP filtering that you should make sure isn’t getting in the way. If there is a firewall blocking localhost, you might get a general failure when you ping the above IP address. This has bitten me before!

Inside of folders: and sites:, let’s make updates.

folders:
    -
        map: C:/Users/[username]/Sites/[homestead_container]
        to: /home/vagrant/code
        type: "nfs"

The first major thing I got stuck on is how to update the map value above. In this section, map is the local directory that contains your project and to is a directory inside of the Vagrant VM that mirrors the local machine. It’s syncing both ways, so you can make changes locally in your editor or you can SSH into Vagrant and make edits on the mirrored files and they will automagically stay in sync.

We can’t use this UNIX-based directory structure syntax since this is Windows

/mnt/c/Users/[username]/Sites/[homestead_container]

We have to use the inherent Windows structure syntax

C:\Users\[username]\Sites\[homestead_container]

EXCEPT for some reason the backward slash must be converted to forward slash!

C:/Users/[username]/Sites/[homestead_container]

When running the Vagrant virtual machine and trying to use my browser to go to the domain name specified inside of this file, all that would come up in the browser window

No Input File Specified

I couldn’t find any documentation on what was going on until I studied this Homestead installation tutorial and saw his use of the forward slash. It was such a small nuance that was easy to overlook!

Laravel briefly touches on the inclusion of NFS, but after a little research, I found that including NFS is a good idea for speed optimization. With that, we need to add NFS support to Vagrant in Windows.

vagrant plugin install vagrant-winnfsd

Using Homestead on Windows will probably be slow. Using it with WSL will probably be just as slow. When I say slow, it can take the browser anywhere from just a few seconds to anywhere up to 20-30 seconds just to start loading the asset files like CSS and JS. My average is about 7-10 seconds to get to that point. That’s really bad and I hope this improves as I learn more. With this in mind, there are some tweaks that can be made to speed up Vagrant inside of [homestead_folder]\vendor\laravel\homestead\scripts\homestead.rb with regard to NFS.

Look for a line that looks like the following:

mount_opts = folder['mount_options'] ? folder['mount_options'] : ['actimeo=1', 'nolock']

The last two array items are two mount_options and the article on speeding up Vagrant shows more possible options that you can add to that array to help speed up access and read times.

This whole section took a week to wrap my head around so I’m glad I could document it for others using Windows!

Let’s continue.

Map the updated domain name to the correct public directory inside of craft

sites:
    -
        map: craft.test
        to: /home/vagrant/code/craft/web

Change map to your preferred domain name. I’d suggest not using .dev for anything since Google now owns this and it can resolve on the regular internet. This is why I kept .test.

to needs to read the public directory inside of Craft. Older versions of Craft show a public directory outside of the craft folder. The latest versions of Craft 3 show the public directory as web inside of the Craft folder.

Finally, update the database

databases:
    - craft

If you didn’t already install vagrant-hostmanager, you’ll need to update your HOSTS file so that the virtual host name will resolve in the browser. Otherwise, vagrant-hostmanager is smart enough to do this for you.

192.168.10.10 craft.test

Optionally, I recommend enabling MariaDB for MySQL. It’s more efficient in several ways, enough so that it’s worth enabling every time.

mariadb: true

Turn on Homestead

Run Vagrant

vagrant up

This will take a few seconds to provision everything with the virtual machine and get its contained server up and running. Once completed, go to your browser and let’s go to the URL.

http://craft.test/

If everything was set up correctly, you should now see a 503 error that indicates the database isn’t set up correctly. If you don’t see something like a 503 error, or if you see the same thing I got earlier saying No Input File Specified, revisit the above sections to make sure everything was updated correctly.

Configure the Database

If you need to import a specific database, follow these instructions to import your database into Vagrant.

For new Craft installations, update the environment file inside of Craft. In your editor, open the craft directory and find the .env file. We have to make a couple of edits to the credentials.

# The database username to connect with
DB_USER="homestead"

# The database password to connect with
DB_PASSWORD="secret"

# The name of the database to select
DB_DATABASE="craft"

This should now allow you to get into the Craft installation page

http://craft.test/index.php?p=admin/install

Credits