Matthew Daly's Blog

I'm a web developer in Norfolk. This is my blog...

11th March 2020 9:20 pm

Caching the Laravel User Provider With a Decorator

A couple of years ago I posted this article about constructing a caching user provider for Laravel. It worked, but with the benefit of hindsight I can now see that there were a number of issues with this solution:

  • Because it extended the existing Eloquent user provider, it was dependent on the internals of that remaining largely the same - any change in how that worked could potentially break it
  • For the same reason, if you wanted to switch to a different user provider, you’d need to add the same functionality to that provider, either by writing a new provider from scratch or extending an existing one

I’ve used the decorator pattern a few times in the past, and it’s a good fit for situations like this where you want to add functionality to something that implements an interface. It allows you to separate out one part of the functionality (in this case, caching) into its own layer, so it’s not dependent on any one implementation and can wrap any other implementation of that same interface you wish. Also, as long as the interface remains the same, there likely won’t be any need to change it when the implementation that is wrapped changes. Here I’ll demonstrate how to create a decorator to wrap the existing user providers.

If we only want to cache the retrieveById() method, like the previous implementation, the decorator class might look something like this:

<?php
namespace App\Auth;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Cache\Repository;
final class UserProviderDecorator implements UserProvider
{
/**
* @var UserProvider
*/
private $provider;
/**
* @var Repository
*/
private $cache;
public function __construct(UserProvider $provider, Repository $cache)
{
$this->provider = $provider;
$this->cache = $cache;
}
/**
* {@inheritDoc}
*/
public function retrieveById($identifier)
{
return $this->cache->remember('id-' . $identifier, 60, function () use ($identifier) {
return $this->provider->retrieveById($identifier);
});
}
/**
* {@inheritDoc}
*/
public function retrieveByToken($identifier, $token)
{
return $this->provider->retrieveById($identifier, $token);
}
/**
* {@inheritDoc}
*/
public function updateRememberToken(Authenticatable $user, $token)
{
return $this->provider->updateRememberToken($user, $token);
}
/**
* {@inheritDoc}
*/
public function retrieveByCredentials(array $credentials)
{
return $this->provider->retrieveByCredentials($credentials);
}
/**
* {@inheritDoc}
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return $this->provider->validateCredentials($user, $credentials);
}
}

It implements the same interface as the user providers, but accepts two arguments in the constructor, which are injected and stored as properties:

  • Another instance of Illuminate\Contracts\Auth\UserProvider
  • An instance of the cache repository Illuminate\Contracts\Cache\Repository

Most of the methods just defer to their counterparts on the wrapped instance - in this example I have cached the response to retrieveById() only, but you can add caching to the other methods easily enough if need be. You do of course still need to flush the cache at appropriate times, which is out of scope for this example, but can be handled by model events as appropriate, as described in the prior article.

Then you add the new decorator as a custom user provider, but crucially, you need to first resolve the provider you’re going to use, then wrap it in the decorator:

<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Contracts\Auth\UserProvider;
use Auth;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Cache\Repository;
use App\Auth\UserProviderDecorator;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('cached', function ($app, array $config) {
$provider = new EloquentUserProvider($app['hash'], $config['model']);
$cache = $app->make(Repository::class);
return new UserProviderDecorator($provider, $cache);
});
}
}

Finally, set up the config to use the caching provider:

'providers' => [
'users' => [
'driver' => 'cached',
'model' => App\Eloquent\Models\User::class,
],
],

This is pretty rough and ready, and could possibly be improved upon by allowing you to specify a particular provider to wrap in the config, as well as caching more of the methods, but it demonstrates the principle effectively.

By wrapping the existing providers, you can change the behaviour of the user provider without touching the existing implementation, which is in line with the idea of composition over inheritance. Arguably it’s more complex, but it’s also more flexible - if need be you can swap out the wrapped user provider easily, and still retain the same caching functionality.

12th February 2020 10:40 pm

The Trouble With Integrated Static Analysis

I’ve always been a big fan in general of tools that provide feedback about the quality of my code. The development role in which I spent the most time was one in which I had no peer feedback or mentoring at all, and while I could definitely have done with more peer review than I had, automated tools helped fill the gap a little bit. When I started building my first Phonegap app, about a year after I started programming professionally, it used far more Javascript than I’d ever used before, and JSLint was very helpful in instilling good practices at that early stage in my career.

In addition, I often find that using an automated tool largely eliminates the issue of ego - if your colleague Bob tells you something is a bad practice, you can potentially dismiss it as “That’s just Bob’s preferences”, whereas an automated tool is potentially much more objective. Nowadays, my typical set of static analysis tools on a project includes:

  • ESLint
  • Flow
  • PHP CodeSniffer
  • Psalm

However, I’m always dubious of using any static analysis tool that’s tightly integrated with a particular editor or IDE. In this post, I will explain my reasoning.

In-editor feedback

Having instant feedback on the quality of your code is tremendously useful. Sure, you can run something like CodeSniffer from the command line and see what the problems are, but that’s nowhere near as useful as having it actually in your code. If you work on a legacy code base, there’s no way in hell you can wade through a long list of output in the terminal and fix them without losing the will to live. By comparison, actually seeing something flagged as an error where it actually occurs makes the mental cost of fixing it much smaller - you can see it in context, and can usually therefore resolve it more easily.

However, that doesn’t explicitly require that any one tool form an integral part of the editor. Most editors can hand off linting and static analysis to other, standalone tools, and doing so offers the following advantages:

  • Less dependence on a given development environment - it’s always a struggle if you wind up stuck using a development environment you dislike (I grew to utterly despise Netbeans in my first role), but if you can use generic feedback tools that can be integrated with just about any editor, your team can use the development environment that suits them most, while still all benefiting from the feedback these tools provide
  • These tools tend to be open source, meaning you have the security of knowing that if the creator ceases maintaining it, either someone else may pick up the baton, or you can choose to fork it yourself. If a commercial IDE provider ceases trading, it’s likely you won’t be able to use their offering at all at some point in the future.

Nowadays I use vim-ale in Neovim, and that provides real-time feedback for a number of linters and static analysis tools, including all those I mentioned above. I have comprehensive information on any issues in my code, and because any configuration is in simple text files that form part of the repository, it’s easy to update those settings for all developers working on the project to ensure consistency.

It’s possible that an integrated solution might offer a few advantages in terms of tighter integration with autocompletion and other functionality allowing for it, but whether they outweigh the tradeoffs mentioned here is dependent entirely on the implementation and how useful it is for any one team.

Continous integration to the rescue

There’s another issue I have with this sort of tightly integrated static analysis, which is probably the biggest, and that is that the feedback is available only at the level of an individual developer, not the team.

It’s great providing all this feedback to developers, but what if they just ignore it? Not all developers have had the sort of experience that leads one to really appreciate the value of coding standards and type hints, particularly if they’ve worked primarily on small or greenfield projects, or in environments where the emphasis was on churning out large quantities of work, and getting developers to tidy up the sort of issues these tools identify can sometimes be a tough sell when faced with code which, at least superficially, works.

Suppose you take on a new developer and ask them to work alone on a particular project for several months. Due to your own workload you can’t easily schedule code reviews with them, so you don’t see what they’re writing until they’re done. Then you take a look at what they’ve written and it’s full of issues that the IDE caught, but the developer either didn’t bother to fix, or didn’t know how to. What they’ve done may well work, but they’ve introduced a huge morass of technical debt that will slow down future development for the foreseeable future.

If your static analysis tools work only in the context of a given editor or IDE, then if the new dev introduce issues in the code base and doesn’t resolve them because they don’t know how, or don’t see the value, then the first you knows about it is when you clone the repo yourself and open it up. With a solution that runs in a CI environment, you can catch any reduction in code quality when it’s pushed. Sure, code reviews can do that too, but that requires manual input in a way that not every team is willing to spare, whereas a CI server, once set up, is largely self sustaining. And you could run one tool locally and another in a CI environment, but you can’t be sure they’ll necessarily catch all the same issues.

Now consider the same scenario if you’re using a separate code quality tool that’s integrated both into the editor, and your continuous integration workflow. Obviously, it will depend on your personal CI setup, but once code quality either begins to drop, or drops below a given level, the CI server will mark the build as failed, and you’ll be alerted. You can therefore then raise the issue with the new dev before it gets out of hand, and provide whatever support they need to resolve the problem there and then.

I personally maintain a legacy project in which, at one point prior to my arrival, a junior dev introduced an enormous amount of technical debt after working on it alone for six months. An integrated linter or static analysis tool probably wouldn’t have stopped that from happening, for the reasons stated above, but if a similar tool were part of the CI workflow, it could have been flagged as an issue much earlier and dealt with. Yes, leaving a junior dev unsupported and unsupervised for that length of time isn’t a great idea, but it happens, particularly in busy environments such as agencies. A good CI setup lets you see if someone is adding these kinds of issues, and act to nip it in the bud before it becomes too much of a problem, which is ultimately good for that developer’s career.

Peer pressure can also be a strong motivating factor under these circumstances. By simply displaying a metric, you encourage people’s natural competitiveness, so displaying code quality stats in your CI dashboard will encourage your team to do better in this regard, and no-one wants to be visibly seen to be letting the team down by producing substandard code.

For these reasons, where possible for feedback on code quality, I would always prefer to rely on a standalone tool that can be integrated with an editor, or used as part of a continuous integration workflow, as opposed to any IDE-specific functionality.

9th February 2020 10:10 am

Don't Use Stdclass

The digital agency I work for specialises in marketing, so some of my work tends to relate to mailing lists. This week I was asked to look at an issue on a Laravel-based export system built by a co-worker who left a few months ago. This particular application pulled data from the Campaign Monitor API about campaigns, and it returned the data as instances of stdClass, something that I’m not terribly happy about.

Now, this was an implementation detail of the Campaign Monitor PHP SDK, which is old and creaky (to say the least…) and doesn’t exactly abide by modern best practices. However, the legacy application I maintain also does this (there’s a lot of stuff that needs sorting out on it, and sadly replacing the stdClass instances is a long way down the list), so I thought it was an interesting subject for a blog post. I consider using stdClass, even just as a dumb container for data, to be an antipattern, and I thought it would be useful to explain my thoughts on this.

Why shouldn’t I use stdClass?

Readability

One of the first things I learned about throwing exceptions is that they should be meaningful. It’s trivial to define a named exception and use that to specify the type of exception, and you can then capture exceptions to handle them differently elsewhere in the application. For instance, a validation error is entirely due to a user submitting the wrong details, and should therefore be handled in an entirely different manner to the database going down.

The same is applicable to an object. If an API client returns an instance of stdClass, that doesn’t tell me anything about what that object is. If I need to pass it elsewhere in a large application, it may become very difficult to understand what it represents without tracking it back to where it came from, which will slow me down. If instead I use a named class, the name can convey what it represents. It may seem like overkill to create a class that adds no new functionality, but the mere fact that it has a name makes your code more meaningful and easier to understand. I can also add DocBlock comments to describe it further.

Of course, just giving something a generic name like Container isn’t much of an improvement, and coming up with meaningful names for classes and methods is notoriously hard. As always, give some serious thoughts into what your objects represent and attempt to give them names that will make it easy to understand what they are if you look at the code base again six months down the line.

Type hinting

A related argument is that it makes type hinting more useful. You can type hint stdClass, but as noted above it tells someone working on the code receiving it very little about where it’s come from, or what it represents, and it doesn’t offer much value since an stdClass could mean anything, and could be created anywhere in the appication.

By contrast, named classes provides much more information about what the type hinted parameter represents. For instance, naming your class something such as App\Api\Response\Item, makes it much clearer that that object represents an individual item returned from an API, and others developers working on the same code base (including your future self, who may not necessarily remember all of the details of how you’re implementing it now), will have less trouble understanding what is going on. There’s also a much-reduced likelihood of the same class being used to represent completely different types of data.

New functionality

Finally, are you sure you don’t want to add any functionality? PHP includes a number of interfaces that can be extremely useful for working with this sort of data.

First off, the ArrayAccess interface allows you to access an object’s values using array syntax, which can be useful. Also, implementing either Iterator or IteratorAggregate will allow you to iterate over the object using a foreach loop. The Countable interface is less useful, since all it does is let you get the number of items, but it’s sometimes handy. Finally, the Serializable interface lets you serialize an object so it can be stored in a suitable medium, which can sometimes be useful.

The same also applies to some of the magic methods. The __toString() method, in particular, can be useful for returning a suitable string-based representation of an object - for instance, if it represents an item in a database, it might be appropriate to use this to return the ID of the item, or a text representation of it (eg title for a blog post, or product name for a product in an e-commerce site). The __get() and __set() magic methods may be a bit more dubious, but they can be useful if your object is intended to just be a dumb container as they allow you to make the properties on the object private, but keep them accessible without writing explicit getters and setters. I’d also suggest adding __debugInfo() to objects unless you have a good reason not to, as when you’re debugging it can be hard to see the wood for the trees, and returning only the most pertinent data can make your job a lot easier.

Of course, you don’t have to implement all this functionality from scratch for every class. It often makes sense to create an abstract class that implements this sort of basic container functionality, and then base all your container classes on that, overriding it when necessary.

Summary

Hopefully, this has made it clear how compelling it is to use named classes instead of stdClass, and how much benefit you can get from not just using named classes, but creating your own base container class for them. I’m of the opinion that PHP should probably make stdClass abstract to prevent them from being used like this, and indeed I’m seriously considering the idea of creating a Codesniffer sniff to detect instances of stdClass being instantiated and raise them as an error.

25th January 2020 10:25 pm

F*** Phpstorm Man and the High Horse He Rode in on

There’s a particularly unpleasant type of programmer that exists, and you’ve probably met him, either online or in person. I call him PHPStorm Man.

NB: Despite the name I’ve chosen, I’m not singling out users of PHPStorm in particular about this. The first time I encountered PHPStorm Man in person, he was using Sublime Text, and you will find PHPStorm Men using all different editors and IDEs. PHPStorm Man is an archetype defined not by any particular piece of software, but by a common bad attitude, and given that I work primarily with PHP these days, I’ve most often encountered this kind of behaviour from PHPStorm users (or at least, people claiming to be PHPStorm users, since at least online you can’t discount the possibility that they’re trolls). Users of other languages may well see this behaviour most prominently from those who use some other editor or IDE, but the same underlying phenomenon is at work, whether we call him PHPStorm Man, Eclipse Man, Vim Man or PyCharm Man.

Who is PHPStorm Man?

The hero we really, really don't need

PHPStorm Man (and he will almost certainly be a man - while it could be just because our industry is male-dominated, I’ve never known a woman to behave like this, and I strongly suspect it’s nothing more than an industry-specific example of the common phenomenon of mansplaining) doesn’t know squat about your editor or IDE. He just knows his is superior, and he wants you to know it, regardless of the evidence.

His knowledge of your editor is probably either non-existent, grossly outdated, plain ill-informed or second-hand (probably from another PHPStorm Man). If he’s advocating an IDE, he’ll likely equate all text editors with Notepad - he may claim the advantages of his one over yours include such fundamentals as syntax highlighting and autocompletion.

He’ll boast about some feature his editor has that yours doesn’t, even if it does. If your editor lacks that functionality out of the box, but can add it via a plugin, apparently that doesn’t count because although you’re intelligent enough to build a working web app, somehow installing and configuring that plugin is an insurmountable burden (yet mysteriously turning off all the stuff you don’t need in his editor is quick and easy). If it does do that out of the box, he’ll probably find some bullshit reason why his editor’s implementation is better, even if it’s something as pointless and irrelevant as “it’s been around longer”. He’ll likely claim, with absolutely no evidence whatsoever, or indeed in the presence of evidence to the contrary, that you’d be more productive if you only used his editor.

In short, if you aren’t using his editor or IDE of choice, you’re a troglodyte living in a dung hut.

PHPStorm man in the wild

I had an encounter with PHPStorm Man in person a while back. Just over two years ago I started a new job, which as it turned out didn’t last long after I caught the flu that was going around in late 2017 my first week. On the second day, shortly after going over something with me, the senior dev sent me the following message on Slack:

I noticed you’re using Vim. Have you tried using Sublime Text?

I responded that I had, and chose not to use it. There followed a long string of messages along the following lines:

Him: Sublime Text has X!

Me: I have that

Him: Well Sublime Text also has Y!

Me: I have that too, via a plugin

Him: Well, Sublime Text doesn’t need a plugin for that

Me: Irrelevant since the plugin is already installed and configured, and I know how to use it

Him: Well, what about this?

Me: That sounds cool, so I just found a plugin to do that and installed it

Him: And this?

Me: I have absolutely no need for that

Him: Well, Vim is old, Sublime Text is new!

Me: Actually, this is Neovim, which is technically newer than Sublime Text

Him: Well, Sublime Text is a GUI application

Me: Exactly. That makes it slower and forces me to use the mouse, aggravating my RSI. I use the terminal because it’s more efficient

Him: Well, I don’t mind what you use…(despite the evidence of that entire conversation)

With the benefit of hindsight, what I should have responded with was this:

I’m an experienced, professional web developer of over six years, and I chose my editor based on years of personal experience, and have chosen my plugins and configuration based on what’s useful to me, and continue to do so to this day. I don’t appreciate you talking down to me like a child.

Why I personally don’t use an IDE

In my case, I have a particularly good reason not to use any GUI application to develop in. Before I was a developer, I worked for an insurance company in a customer service role, and I didn’t have access to the sort of decent quality keyboards and mice developers habitually use, as well as having output goals linked to discipline and bonus/salary raises and having to use custom internal applications on Windows XP, with dreadful keyboard support. As a result I developed a degree of RSI in both hands, which I’ve found is aggravated by using any application that requires me to use a mouse extensively - I’m generally OK if I only have to type, but reaching for the mouse all the time quickly becomes tiring, and soon after painful.

For that reason I’ve developed a workflow that’s strongly dependent on the command line - I use Neovim in the terminal, alongside Byobu so that I can run multiple tabs and switch between them quickly without touching the mouse. Moving to a more GUI-oriented workflow would require me to use the mouse more than I do now, which would probably become physically painful quite quickly. Using an editor or IDE which I found made me more prone to further flare-ups of RSI could have serious consequences for my long-term health, and could potentially be career-ending. If I worked somewhere that mandated a particular IDE that didn’t work well for me, I’d have to either negotiate an exception on health and safety grounds or quit.

I’m also of the personal opinion that much of the functionality of an IDE should not be, in principle, tied to that IDE, but should instead be the province of more general purpose tools that can used, not merely in any editor or IDE but, where appropriate, on a continuous integration server. For instance, language servers provide a tool-agnostic way for any IDE or editor to implement functionality such as completion or navigation, and linters such as ESLint can integrate into any half-decent editor or run in a CI environment. Since these tend to be open source projects, whereas IDE’s are normally commercial offerings, they’re less vulnerable to suddenly disappearing and leaving users high and dry.

There’s also a lot of functionality in an IDE that I rarely, if ever, use. There’s no point including and starting up an FTP client as part of my editor if I’m never going to use it, as it slows the application down, and nor should I have to root around trying to turn off functionality I’m never going to have to use. For a lot of other functionality, there are more powerful standalone applications that I’m used to such as Postman or MySQL Workbench, and I’ll use them as and when I need them - I gain nothing by having them integrated with my editor.

I also like to be able to use the same editor everywhere. I still occasionally dabble in Python, so a language-specific IDE wouldn’t be suitable when switching between languages. I also sometimes work on personal projects on an HP Stream netbook running Xubuntu, which is fine for small PHP projects that don’t require a database server or any web server other than the PHP dev server. I can happily run Neovim on that, but there’s no way it could run an IDE at an acceptable speed.

Last of all, screen real estate is an issue. I don’t like interfaces that are too busy - I cannot stand having anything, at all on my desktop for any length of time at all, and any interface that has too much on screen at once is distracting. I will typically have Neovim open in a terminal, with the NERDTree file finder open on the left, and two panels split in the main body, and that’s all. A big factor in my productivity is how much code I can see at once, and having too much screen real estate taken up by menus and sidebars is counterproductive - with Neovim there’s almost nothing getting in the way.

I personally have had to give this sort of explanation many, many times as to why I use first Vim and then Neovim, and indeed part of the motivation behind writing this post is that I’m sick to death of having to explain myself over and over again and will now be able to merely direct them to this article. Thanks to tools like PHPActor, vim-ale and FZF, I don’t feel like there’s anything I’m missing out on that an IDE would give me, and Psalm is very good at catching type errors without being tied to any one IDE, but that doesn’t stop people telling me I’m missing out on features I already have. Any time I come across a feature I think is cool, I go through the following process:

  • Find cool feature
  • Find plugin that implements said feature
  • Install plugin by adding a single line to my Neovim config and running :PlugUpdate
  • Add a few lines of config
  • Start using feature

Using an IDE would eliminate the middle three steps, but I don’t find those onerous - we’re talking about the work of five minutes, which is insignificant compared to the time taken to learn to use the feature effectively. And a feature you don’t use is one that you still have to start up if it’s present, so making it an opt-in plugin is often a better way to go.

Every other developer will have their own version of this story. Some will have stayed mostly static in their editor choices, while others will be changing all the time - indeed, I’ve sometimes used other editors for specific tasks in the past. In short, everyone has their own reasons for using their editor of choice, and it’s appallingly arrogant to assume that their reasons for using a particular one are less valid than yours.

Am I PHPStorm Man?

As I’ve said before, this behaviour is not confined to PHPStorm users, nor is it in any way universal among them. If you use PHPStorm and enjoy it, then fine, rock on. If you use a different editor or IDE, then that’s fine too - I don’t have a problem with that, and nor should your colleagues or line manager. Using any one editor or IDE does not make you PHPStorm Man. What makes you PHPStorm Man is the patronising attitude.

In the example given above, what made the senior dev PHPStorm Man was not the initial enquiry as to whether I’d tried Sublime Text, but the fact that he wouldn’t leave it be when confronted with evidence that I either had, could easily obtain, or didn’t need the functionality of his editor in mine, and that he was talking down to an experienced developer like a child.

Obviously, this isn’t a new development - editor wars have long been a feature of our industry, as has the divide between IDE and editor users. But that doesn’t mean I, and no doubt others, don’t get utterly sick of it.

How not to be PHPStorm Man

When talking to users of other editors or IDE’s about the subject of those tools, you should always bear this in mind:

  • If they use a different tool to you, they probably know a hell of a lot more about it than you do, and are unlikely to take kindly to you ignorantly telling them what it can and can’t do
  • Mastering an editor or IDE can take years, and if they’re already invested in one, it’s incredibly arrogant to just assume that they’re less productive in it than they would be in yours - even if they would (and that’s almost certainly debatable), it would take some time to adjust.
  • They’ve probably had this conversation many times before, and are sick of hearing it, especially if they have a few years experience under their belt
  • Not every feature you use is useful to them
  • No-one minds seeing a cool feature, so feel free to demonstrate it, but bear in mind that it’s almost certainly not limited to that platform - if it’s suffficiently cool, someone will have made it available as a plugin on most of the major editors and IDE’s. If they like it, the most likely scenario is that they’ll look to add that feature to their own editor via a plugin
  • Just because it makes you more productive, doesn’t mean that it would make them more productive
  • It’s perfectly possible to enforce consistent code styles and catch errors using standalone tools such as PHP CodeSniffer, Psalm, or ESLint, and these tools can be integrated in any editor, triggered with Git hooks, or run with continuous integration.

Now, it has to be said that sometimes there are some people who plod on with painfully outdated tools, like Notepad. But those tools tend to be limited to either commercial offerings that are no longer mainained or supported, or ones that lack any sort of plugin or extension system, making them limited in terms of how they can integrate with other services, so they’re fairly easy to spot. However, making a particular editor or IDE compulsory is going to be disruptive. If you’re in a leadership position, one way to resolve this is to simply require that everyone’s editor have certain functionality - for instance, if you specify that everyone’s editor must allow integration with PHP CodeSniffer and support for .editorconfig, then anyone using a legacy editor that can’t support those will need to move away from it, but they’ll be able to pick one that suits them, rather than be forced into one they may well dislike. Editors and IDE’s don’t produce proprietary formats the way word processors do - they work with common formats, and if prominent open-source projects can enforce a consistent coding standard with many different editors there’s absolutely no reason why your colleagues can’t do so too,

Summary

This post is a bit of an angry rant, but at the same time it shouldn’t be taken too seriously. As I said, despite the name PHPStorm Man, it’s not specifically about users of any one editor or IDE, but about the widespread, patronising attitude many developers have about editors and IDE’s other than their own in general.

Someone using a different IDE or editor is absolutely none of your business unless you’re their line manager or you work on the same code base, and even then it should only be an issue if it causes a clear effect on their productivity or the quality of their code. If that’s not the case, keep your nose out.

27th October 2019 9:20 pm

Input Components With the Usestate and Useeffect Hooks in React

Like many developers who use React.js, I’ve been eager to explore the Hooks API in the last year or so. They allow for easier ways to share functionality between components, and can allow for a more expressive syntax that’s a better fit for Javascript than class-based components. Unfortunately, they became production ready around the time I rolled out a new React-based home page, so I didn’t want to jump on them immediately in the context of a legacy application. I’m now finding myself with a bit of breathing space, so I’ve begun refactoring these components, and converting some to use hooks, in order to more easily reuse some code that currently resides in a big higher-order component.

The useState and useEffect hooks are by far the most common hooks in most applications. However, I’ve found that the React documentation, while OK at explaining how to use these individually, is not so good at explaining how to use them together, particularly in the case of an input component, which is a common use case when looking to convert existing components. For that reason, I’m going to provide a short example of how you might use them together for that use case.

A simple function component

A basic component for an input might look like this:

//@flow
import React from 'react';
type Props = {
name: string,
id: string,
value: string,
placeholder: string
};
const Input = (props: Props) => {
return (
<input type="text" name={props.name} id={props.id} value={props.value} placeholder={props.placeholder} />
);
}
export default Input;

Note I’m using Flow annotations to type the arguments passed to my components. If you prefer Typescript it should be straightforward to convert to that.

As you can see, this component accepts a name, ID, value and placeholder as props. If you add this to an existing React app, or use create-react-app to create one and add this to it, you can include it in another component as follows:

<Input name="foo" id="foo" value="foo" placeholder="foo" />

Adding state

This will render, but as the value will never change it’s not actually of any use in a form. If you’ve written class-based React components before, you’ll know that the usual way to handle this is to move the value of the input from props to state. Prior to the introduction of the Hooks API, while you could create a function component, you couldn’t use state with it, making situations like this difficult to handle. Fortunately, the useState hook now allows you to add state to a function component as follows:

//@flow
import React, { useState } from 'react';
type Props = {
name: string,
id: string,
value: string,
placeholder: string
};
const Input = (props: Props) => {
const [value, setValue] = useState(props.value);
return (
<input type="text" name={props.name} id={props.id} value={value} placeholder={props.placeholder} onChange={(e) => setValue(e.target.value)} />
);
}
export default Input;

We import the useState hook at the top, as usual. Then, within the body of the component, we call useState(), passing in the initial value of props.value, and get back two variables in response:

  • value is the value of the state variable, and can be thought of as equivalent to what this.state.value would be in a class-based component
  • setValue is a function for updating value - rather than explicitly defining a function for this, we can just get one back from useState()

Now we can set the value with value={value}. We also need to handle changes in the state, so we add onChange={(e) => setValue(e.target.value)} to call setValue() on a change event on the input.

Handling effects

The component will now allow you to edit the value. However, one problem remains. If you open the React dev tools, go to the props for this component, and set value manually, it won’t be reflected in the input’s value, because the state has diverged from the initial value passed in as a prop. We need to be able to pick up on changes in the props and pass them through as state.

In class-based components, there are lifecycle methods that fire at certain times, such as componentDidMount() and componentDidUpdate(), and we would use those to handle that situation. Hooks condense these into a single useEffect hook that is more widely useful. Here’s how we might overcome this problem in our component:

//@flow
import React, { useState, useEffect } from 'react';
type Props = {
name: string,
id: string,
value: string,
placeholder: string
};
const Input = (props: Props) => {
const [value, setValue] = useState(props.value);
useEffect(() => {
setValue(props.value);
}, [props.value]);
return (
<input type="text" name={props.name} id={props.id} value={value} placeholder={props.placeholder} onChange={(e) => setValue(e.target.value)}/>
);
}
export default Input;

useEffect takes one compulsory argument, in the form of a callback. Here we’re using that callback to set our state variable back to the value of the prop passed through.

Note the second argument, which is an array of variables that should be watched for changes. If we had used the following code instead:

useEffect(() => {
setValue(props.value);
});

Then the callback would fire after every render, reverting the value back and possibly causing an infinite loop. For that reason, we pass through the second argument, which tells React to only fire the callback if one of the specified variables has changed. Here we only want to override the state when the value props passed down to the component changes, so we pass that prop in as an argument.

Summary

This is only a simple example, but it does show how simple and expressive hooks can make your React components, and how to use the useEffect and useState hooks together, which was something I found the documentation didn’t make clear. These two hooks cover a large chunk of the functionality of React, and knowledge of them is essential to using React effectively.

Recent Posts

Caching the Laravel User Provider With a Decorator

The Trouble With Integrated Static Analysis

Don't Use Stdclass

F*** Phpstorm Man and the High Horse He Rode in on

Input Components With the Usestate and Useeffect Hooks in React

About me

I'm a web and mobile app developer based in Norfolk. My skillset includes Python, PHP and Javascript, and I have extensive experience working with CodeIgniter, Laravel, Zend Framework, Django, Phonegap and React.js.