Decorators in .NET Core with Dependency Injection

What is a Decorator?

Feel free to skip this section if you’re already familiar with the Decorator pattern

The Decorator Pattern allows you to add functionality to an implementation of an interface by wrapping it in another implementation. e.g.

interface IService {
  string GetValue();
}

class DbService {
  public string GetValue() => "value from DB";
}

class LoggingService {
  private readonly IService _concreteService;
  private readonly ILogger _logger;

  public LoggingService(IService concreteService, ILogger<LoggingService> logger) {
    _concreteService = concreteService;
    _logger = logger;
  }

  public string GetValue() {
    _logger.LogInformation("Getting value");
    var value = _concreteService.GetValue();
    _logger.LogInformation("Retrieved {0}", value);
    return value;
  }
}

We have a concrete implementation of an IService interface (DbService) that returns a string value. LoggingService then decorates that implementation by wrapping invocation of the concrete instance and logging entry and exit.

This pattern is very useful when you want to augment either your own or framework types with some new behaviour such as logging or exception handling.

Decorators and Dependency Injection

The dependency injection framework in .NET Core is pretty good but out-of-the-box it doesn’t support this pattern very well.

If I register 2 copies of IService then any class attempting to consume IService will receive the one that was registered last. If they try to consume an IEnumerable then they will receive all of them. This is perfectly sensible behaviour but in this case it makes life harder.

Worse, if an implementation of IService depends on an instance of IService (and is the last one registered) then we will get a StackOverflowException when we resolve it!

Ideally we would like to be able to use this pattern with dependency injection without having to put too many constraints on either the decorator or the concrete implementation.

Specifically:

  • the wrapped implementation should be able to be registered in the DI container against the same interface as the decorator. This avoids adding any requirements to the code we are decorating
  • the decorator should depend on an instance of the interface it implements. This allows decorators to be applied to other decorators or implementations and makes the decorator easily testable
  • any dependencies of either the concrete implementation or the decorator should be injected automatically

So how can we achieve all this?

Approach #1: Reference a Concrete Type

Have the decorator explicitly depend on a concrete implementation of the wrapped class.

class Decorator : IService {
  public Decorator(ConcreteService service) {}
}

public static void ConfigureServices(IServiceCollection services) {
  services.Replace(ServiceDescriptor.Scoped<IService, Decorator>());
}
  • ✅ Easy
  • ❌ Can only decorate one implementation
  • ❌ Cannot inject a mock for unit tests
  • ❌ Requires the decorated implementation to be registered against it’s concrete type – not the interface
  • ❌ Cannot take service lifetime (scoped, transient or singleton) from wrapped service

Overall, it’s not great.

Approach #2: Use a Factory Registration

Use a factory method to pass the explicit concrete type to the decorator constructor.

class Decorator : IService {
  public Decorator(IService concreteService) {}
}

public static void ConfigureServices(IServiceCollection services) {
  services.Replace(ServiceDescriptor.Scoped<IService>(s =>
    new Decorator(s.GetRequiredService<ConcreteService>())
  ));
}
  • ✅ Decorator can construct on an interface
  • ❌ Still requires decorated implementation to be registered against it’s concrete type
  • ❌ Forces you to manually maintain other dependencies for Decorator
  • ❌ Cannot take service lifetime from wrapped service

This solution is relatively neat until your Decorator class starts to gain other dependencies.

class Decorator : IService {
  public Decorator(
    IService concreteService,
    IOtherService1 anotherDependency,
    IOtherService2 yetAnotherDependency) {}
}

public static void ConfigureServices(IServiceCollection services) {
  services.Replace(ServiceDescriptor.Scoped<IService>(s =>
    new Decorator(
      s.GetRequiredService<ConcreteService>(),
      s.GetRequiresService<IOtherService1>(),
      s.GetRequiredService<IOtherService2>())
  ));
}

It’s not the end of the world but it is a bit of a headache having to manually update constructor calls when you change a signature.

This approach also fails our requirement that the wrapped implementation is not explicitly specified.

Approach #3: Reusing the Existing Service Descriptor

IServiceCollection is largely a wrapper around a list of ServiceDescriptor objects so we can go grab the existing registration and use that in our factory.

class Decorator : IService {
  public Decorator(
    IService concreteService,
    IOtherService1 anotherDependency) {}
}

public static void ConfigureServices(IServiceCollection services) {
  var wrappedServiceDescriptor = services.First(s => s.ServiceType == typeof(IService));

  services.Replace(ServiceDescriptor.Describe(
    typeof(IService),
    s => new Decorator(
      s.CreateInstance(wrappedServiceDescriptor),
      s.GetRequiresService<IOtherService1>()),
    wrappedServiceDescriptor.Lifetime
  ));
}

public static object CreateInstance(this IServiceProvider services, ServiceDescriptor descriptor)
{
  if (descriptor.ImplementationInstance != null)
    return descriptor.ImplementationInstance;

  if (descriptor.ImplementationFactory != null)
    return descriptor.ImplementationFactory(services);

  return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType);
}

Here we use the existing service descriptor to resolve an instance that we can pass into our constructor, as well as copying the defined service lifetime. We’re using the CreateInstance helper method to create an instance of the wrapped type from the service descriptor.

  • ✅ Decorator can construct on an interface
  • ✅ No longer requires the decorated implementation to be registered agaist it’s concrete type
  • ✅ Copies service lifetime from wrapped service
  • ❌ Still forces you to manually maintain other dependencies for Decorator

Closer, but we still have the pain point of managing dependencies when Decorator changes in the future.

Approach #4: Use an Intermediate Scope

Use the dependency injection framework to instantiate the decorator from the services registered so far.

//Decorator class as above

public static void ConfigureServices(IServiceCollection services) {
  var wrappedServiceDescriptor = services.First(s => s.ServiceType == typeof(IService));

  // add the decorator as a concrete type (with the same lifetime as the wrapped service)
  services.Add(ServiceDescriptor.Describe(typeof(Decorator), typeof(Decorator), wrappedServiceDescriptor.Lifetime);

  // build a new service provider that contains the decorator
  // as a concrete type and the wrapped service as the interface
  // (as well as anything that has already been registered)
  var intermediateProvider = services.BuildServiceProvider();

  // replace the interface registration by resolving the
  // decorator type from the intermediate provider
  services.Replace(ServiceDescriptor.Describe(
    typeof(IService),
    s => intermediateProvider.GetRequiredService<Decorator>(),
    wrappedServiceDescriptor.Lifetime
  ));
}
  • ✅ Decorator can construct on an interface
  • ✅ Can register decorated implementation against interface
  • ✅ Automatically handles dependency changes
  • ❌ Can cause scope issues with other dependencies

This version is pretty good but it does have a subtle problem: the intermediate scope will only contain services that have been registered at the time this code is called. This means that if either Decorator or the wrapped service have other dependencies that are registered later then they won’t be available.

This is possibly not the worst problem in the world but it could lead to a nasty category of bug that would be very difficult to track down. Can we do better?

Going to the Source

By this point we can define our ideal solution as something like:

var existingService = services.First(s => s.ServiceType == typeof(IService));
services.Replace(ServiceDescriptor.Describe(
  typeof(IService),
  s => s.GetRequiredService<Decorator>(/* somehow pass in existingService here */),
  existingService.Lifetime)
);

i.e. we want to use the existing service registration but only while resolving the decorator. Anything else resolving IService from the container should get the decorator.

While researching this problem, the closest analogue that came to mind was the way in which HttpClientFactory injects specific HttpClient instances into resolved classes. After a few minutes digging through the source (open source FTW!) I found their implementation including a few useful utilities that can help us out here.

ActivatorUtilities & ObjectFactory

The ActivatorUtilities.CreateFactory helper method creates a factory function for a concrete type:

var objectFactory = ActivatorUtilities.CreateFactory(
  typeof(Decorator), 
  new[] { typeof(IService) });

The second parameter is an array of the types that we can explicitly provide to the created objectFactory function. In this case, we will provide an explicitly-specified instance of IService but every other constructor parameter should be sourced from a service provider.

We invoke the objectFactory by passing in a service provider as well as an array of concrete instances of the types specified above.

ServiceDescriptor wrappedDescriptor;
IServiceProvider services;
var instanceOfDecorator = objectFactory(
  services, 
  new [] { services.CreateInstance(wrappedDescriptor) });

These 2 methods together give us exactly what we need:

  • ✅ Decorator can construct on an interface
  • ✅ Wrapped implementation can be registered against the interface
  • ✅ Decorator scope is taken from wrapped service
  • ✅ Dependencies in wrapped implementation are injected automatically
  • ✅ Dependencies in decorator are injected automatically

Here’s our final implementation, converted into a generic extension method on the IServiceCollection:

public static class ServiceCollectionExtensions {

  public static void Decorate<TInterface, TDecorator>(this IServiceCollection services)
    where TInterface : class
    where TDecorator : class, TInterface
  {
    // grab the existing registration
    var wrappedDescriptor = services.FirstOrDefault(
      s => s.ServiceType == typeof(TInterface));

    // check it's valid
    if (wrappedDescriptor == null) 
      throw new InvalidOperationException($"{typeof(TInterface).Name} is not registered");

    // create the object factory for our decorator type,
    // specifying that we will supply TInterface explicitly
    var objectFactory = ActivatorUtilities.CreateFactory(
      typeof(TDecorator), 
      new[] { typeof(TInterface) });

    // replace the existing registration with one
    // that passes an instance of the existing registration
    // to the object factory for the decorator
    services.Replace(ServiceDescriptor.Describe(
      typeof(TInterface),
      s => (TInterface)objectFactory(s, new[] { s.CreateInstance(wrappedDescriptor) }),
      wrappedDescriptor.Lifetime)
    );
  }

  private static object CreateInstance(this IServiceProvider services, ServiceDescriptor descriptor)
  {
    if (descriptor.ImplementationInstance != null)
      return descriptor.ImplementationInstance;

    if (descriptor.ImplementationFactory != null)
      return descriptor.ImplementationFactory(services);

    return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType);
  }
}

Our sample usage is now nice and easy. We can even stack more than one decorator with each one wrapping the previously-registered type!

public static void ConfigureServices(IServiceCollection services) {
  services.AddScoped<IService, DbService>();
  services.Decorate<IService, LoggingService>();
  services.Decorate<IService, ExceptionHandlingService>(); //etc.

  // IService resolves to:
  //  an ExceptionHandlingService
  //    wrapping a LoggingService
  //      wrapping a DbService
}

I’m not sure why this was such a complicated thing to achieve with the dependency injection framework – it is certainly easier in, for example, Autofac – but it goes to show the great advantage of open source: you can almost always find a way!

Advertisements

Conditional Rendering in React

Conditionally rendering content is a pretty common pattern in React components.

class MyComponent extends Component {
  render() {
    const { user } = this.props;
    return (
      <div>
        {user && <h1>{user.name}</h1>}
        {/* other content */}
      </div>
    );
  }
}

This can quickly get messy when you introduce multiple parmeters or large conditional content blocks

render() {
  const { user, profile, otherData } = this.props;
  const shouldDisplayProfile = user && profile && otherData;
  return (
    <div>
      {shouldDisplayProfile &&
        <div>





        </div>
      }
      {/* other content */}
    </div>
  );
}

And don’t even mention ternary operators. Nothing better than scrolling through a component and finding : null} with no context 👀

Wrapping Dependent Content

In React, our unit of reuse is usually a component so can we move this behaviour into a component?

const example = (
  <OnlyIf all={{ user, profile, otherData }}>
    only render this content if all of user, profile & otherData are defined
  </OnlyIf>
);

It turns out that this is actually a pretty simple component:

  1. determine if the all prop values are truthy
  2. only render the children if they are
interface OnlyIfProps<T> {
  all: T;
}

class OnlyIf<T> extends PureComponent<OnlyIfProps<T>> {
  render() {
    const { all, children } = this.props;

    let allValuesSpecified = true;
    Object.keys(all).forEach(
      key => (allValuesSpecified = allValuesSpecified && !!all[key])
    );

    return allValuesSpecified ? children : null;
  }
}

Lazy Rendering Content

The “ component above may be useful but there is an important scenario that is not covered: children attempting to use undefined values.

const example = (
  <OnlyIf all={{ user }}>
    <h1>{user.name}</h1> {/* <-- this will throw if user is undefined */}
  </OnlyIf>
);

In this case we shouldn’t even attempt to render the children if user is undefined. We cannot prevent it when we use the structure above but if we switch to a render props pattern then we can control when children are rendererd:

const example = (
  <OnlyIf all={{ user }}>{values => <h1>{values.user.name}</h1>}</OnlyIf>
);

We can update our component to support both scenarios by…

  1. accepting either ReactNode (the default) or (values: T) =&gt; ReactNode as our children
  2. identifying the function in render and invoking it
interface OnlyIfProps<T> {
  all: T;
  children: ReactNode | ((values: T) => ReactNode);
}

class OnlyIf<T> extends PureComponent<OnlyIfProps<T>> {
  render() {
    const { all, children } = this.props;

    let allValuesSpecified = true;
    Object.keys(all).forEach(
      key => (allValuesSpecified = allValuesSpecified && !!all[key])
    );

    if (!allValuesSpecified) return null;

    return typeof children === 'function' ? children(all) : children;
  }
}

Now we can accept either a render function or a component as the situation demands!

Is this a good idea?

So we absolutely can create “ but is it useful?

On the negative side, it is an unfamiliar component that may have unexpected effects for new developers.

On the other hand, it’s fairly self explanatory and is an improvement in general readability in my opinion.

Overall I think it’s a nice addition. It certainly beats the hell out of a 20 line ternary expression!

Higher-Order Components with React & TypeScript

Higher-order components are a great way of reusing common functionality in your react app.

TypeScript is a great way of making sure your react app can grow without you losing your mind.

So how can we use the two together?

Higher-Order Components

A higher-order component (HoC) is a function that accepts a component as an argument and returns a new component that has been enhanced in some way.

const EnhancedComponent = myHigherOrderComponent(WrappedComponent);

The enhanced component can add behaviour, manage state or in any other way modify WrappedComponent.

Injecting Props

A common pattern is for a higher-order component to provide some props to the wrapped component, moving the management of those props out of WrappedComponent and allowing it to focus on other concerns.

For example, connect(...)(WrappedComponent) in react-redux provides props to both expose redux state and to dispatch actions. withRouter(WrappedComponent) in react-router similarly injects routing-related props into WrappedComponent from the current route.

Getting the prop types right when injecting props can be a bit fiddly. We want:

  1. WrappedComponent to have new props injected automatically
  2. Injected props not to be required on the created EnhancedComponent
  3. Any other props on WrappedComponent to be forwarded from EnhancedComponent
  4. TypeScript to infer all of the above with no manual specification for the user

For example, let’s say we have a HoC that will manage the isExpanded state of an expander component. It will provide the following props to WrappedComponent

interface ExpanderProps {
  isExpanded: boolean;
  toggleExpanded(): void;
}

Our wrapped component will need to accept these props but it might define some others as well. Let’s say it has a title that needs to be specified and an optional className:

interface WrappedComponentProps {
  isExpanded: boolean;
  toggleExpanded(): void;
  title: string;
  className?: string;
}

note that you could (and should!) define this as WrappedComponentProps extends ExpanderProps... and avoid duplicating the prop definitions. I’ve left this as it is here for clarity

In this case we want both title and className to be available on EnhancedComponent:

const EnhancedComponent = withExpander(WrappedComponent);

const usage = (
  <EnhancedComponent
    title="The title"
    className="class-name">
    ...
  </EnhancedComponent>
);

The Wrapped Component

At this stage we have already defined the provided props as an interface and we have an example of what the wrapped component might look like. Let’s flesh that out a bit (and give it a more appropriate name):

interface ExpanderComponentProps extends ExpanderProps {
  title: string;
}

class ExpanderComponent extends PureComponent<ExpanderComponentProps> {
  render() {
    const { isExpanded, toggleExpanded, title, children } = this.props;

    return (
      <>
        <button onClick={toggleExpanded}>{title}</button>
        {isExpanded && children}
      </>
    );
  }
}

Fairly simple: render a button containing title that toggles the expanded state. If isExpanded=true then also render the children.

Create the Enhancer

Now we want to create the enhancer function that we can use to inject isExpanded and toggleExpanded. Our first requirement is that it accepts an argument of WrappedComponent where WrappedComponent accepts the props we want to inject. That function signature might look something like this:

function withExpander(WrappedComponent: ComponentType<ExpanderProps>) {
  //...
}

ComponentType is a react type that allows you to pass in either a component class or a stateless functional component that has props of T

The next requirement is that it returns a component that doesn’t require the ExpanderProps to be specified, which means that we need to provide them from the enhanced component. In this case we’re going to get those values from the state on the component but that’s not a requirement – they can come from anywhere.

Once we have the values we need we can pass them to WrappedComponent in the render method:

function withExpander(WrappedComponent: ComponentType<ExpanderProps>) {
  return class WithExpander extends PureComponent<{}, { isExpanded: boolean }> {
    render() {
      return <WrappedComponent
        {...this.state}
        toggleExpanded={this.toggleExpanded}
        />
    }

    private toggleExpanded = () => this.setState(state => ({ isExpanded: !state.isExpanded }));
  }
}

All right, nice and easy! Except… build failure!

const EnhancedComponent = withExpander(ExpanderComponent);
// Error!

Our ExpanderComponent requires a title prop; when we use it (as WrappedComponent) in withExpander we don’t specify the title!

We could make withExpander aware of the title prop but that’s not very useful for the future: there will likely be other props that need to be passed through and we don’t want to keep adding them to our HoC.

This brings us on to Requirement 3 above: any other props from the wrapped component should be forwarded from the enhanced component. In this case, we really want to have the title prop be exposed on EnhancedComponent.

The first step in getting to that point is to allow the WrappedComponent parameter of our HoC to accept props that aren’t on ExpanderProps. In fact, we need our wrapped component to accept props of both ExpanderProps and whatever else it wants.

We can introduce a generic type parameter to the HoC function to represent that combined props object:

function withExpander<TWrappedComponentProps extends ExpanderProps>(
  WrappedComponent: ComponentType<TWrappedComponentProps> ) {
  //...
}

That solves our build failure: withExpander will now accept any component as long as it has the two isExpanded and toggleExpanded props.

Sadly we’re not quite done yet though; try to use the enhanced component and you will see our next problem:

const EnhancedComponent = withExpander(ExpanderComponent);

const usage = <EnhancedComponent title="Some title" />;
// Error!

Another build failure, this time telling us that title does not exist on the enhanced component. We’ve managed to write our HoC so that it allows extra props but all it does is ignore them!

What we really want here is to create a new Props type that we can assign to our created component. TypeScript 2.8 includes 2 built-in types that are going to help us out:

  • Pick<T, U> that takes the props from T specified in U. e.g. Pick<ExpanderProps, ‘isExpanded’> creates a type with only the isExpanded property
  • Exclude<T, U> that takes everything from T except U. e.g. Exclude<‘one’ | ‘two’ | ‘three’, ‘two’>* creates a type 'one' | 'three'

By combining these with keyof we can build up the “all props that are on the inner component but excluding the ones we’re providing” type. We might want a better name though…

function withExpander<TWrappedComponentProps extends ExpanderProps>(WrappedComponent: ComponentType<TWrappedComponentProps>) {
  type WrappedComponentPropsExceptProvided = Exclude<keyof TWrappedComponentProps, keyof ExpanderProps>;
  // => 'title' | 'className'
  type ForwardedProps = Pick<TWrappedComponentProps, WrappedComponentPropsExceptProvided>;
  // => { title: string; className?: string }

  //...
}

Now that we know what our forwarded props should look like we can specify those on the component.

return class WithExpander extends PureComponent<
  ForwardedProps,
  { isExpanded: boolean }
> {
  render() {
    return (
      <WrappedComponent
        {...this.props}
        {...this.state}
        toggleExpanded={this.toggleExpanded}
      />
    );
  }

  //...
};

the additional spread of this.props onto WrappedComponent copies the externally-specified props onto the wrapped component

Now we can use our enhanced component as intended.

const EnhancedComponent = withExpander(ExpanderComponent);

const usage = (
  <EnhancedComponent title="title">
    <div>...</div>
  </EnhancedComponent>
);

As a bonus, TypeScript gives us Requirement 4 for free! Type inference means that we don’t have to specify any types explicitly in our usage of this HoC but we still have full type safety. Try removing title or adding an unknown property and your IDE will light up with compilation errors – thanks TypeScript! ✨

Find .NET Core packages in all projects

In a quick aide-memoire for next time I need to use it: here’s a Powershell snippet that will return a list of all unique package names in all .NET Core projects under the current folder:

get-childitem -recurse -filter *.csproj |
  foreach { get-content $_.FullName } |
  select-string -pattern "PackageReference Include=""(.+?)""" |
  % { $_.matches[0].Groups[1].Value } |
  sort-object -unique

Line by line…

  • Get all *.csproj in the current and child folders
  • Read the content of each one
  • Extract every line that matches the specific regex and extract the name of the package
  • Grab the package name group
  • Sort & filter to unique values

This outputs something like

CommandLineParser
CsvHelper
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Console

Generic behaviour in ASP.NET Core with Action Filters

Everyone hates copy/pasting code, and Action Filters in ASP.NET Core MVC offer a great way to avoid filling your controllers with boilerplate.

Filters offer you entry points into the execution pipeline for an action where you can examine the incoming parameters or generated results and modify them to suit your needs.

Here are a couple of examples of how this can help.

Treat a null result as a 404 Not Found

By default an ASP.NET Core controller will return a 204 No Content response if you return null from an action:

[Route("api/example")]
public class ExampleApiController : Controller
{
  [HttpGet("")]
  public string GetExample()
  {
    return null;
  }
}

In some cases, however, you might not want to treat null as No Content. If your API is looking up a resource by ID, for example, then a 404 Not Found response would be more useful:

[HttpGet("{id}")]
public MyDtoObject GetById(int id)
{
  if (!_store.ContainsId(id))
    return null; //should return 404

  //...
}

We can use an action filter to automatically replace the null result with a NotFoundResult:

//filter
public class NullAsNotFoundAttribute : ActionFilterAttribute
{
  public override void OnActionExecuted(ActionExecutedContext context)
  {
    var objectResult = context.Result as ObjectResult;

    if (objectResult?.Value == null)
      context.Result = new NotFoundResult();
  }
}

//controller
[HttpGet("{id}")]
[NullAsNotFound]
public MyDtoObject GetById(int id)
{
  //...
}

Here we override OnActionExecuted to invoke our code after the action method has generated a result but before that result is processed.

If the generated result is an ObjectResult with a null value then we replace it with an empty NotFoundResult and our controller will now return a 404 response.

Treat invalid models as a 400 Bad Request

It is very common to see the following pattern in MVC controllers:

[HttpPost("")]
public IActionResult Create(MyModel model)
{
  if (!ModelState.IsValid)
    return new BadRequestObjectResult(ModelState); //or a View, or other validation behaviour

  //...eventually return created model
  return Ok(model);
}

This has 2 downsides:
* Boilerplate code in every action that needs to validate
* Return type must be IActionResult to accomodate 2 result types

The second point is fairly minor but worth noting. By exposing IActionResult instead of the concrete type we lose metadata about the action.
That metadata is useful for things like generating swagger docs, and losing it can mean you need to decorate the method with response types (though this is improved in ASP.NET Core 2.1 with IActionResult).

In any case, we can make this behaviour generic by moving the validation check into another action filter:

public class InvalidModelAsBadRequestAttribute : ActionFilterAttribute
{
  public override void OnActionExecuting(ActionExecutingContext context)
  {
    if (!context.ModelState.IsValid)
      context.Result = new BadRequestObjectResult(context.ModelState);
  }
}

This time we are overriding OnActionExecuting instead of OnActionExecuted so our code gets run before the controller action. We can tell if the model is invalid before hitting our controller so we can skip the action entirely if we know it should be replaced with a 400.

Other Possibilities

Wherever you find yourself writing duplicate code in many actions it is worth considering whether it can be pulled out into a filter (or middleware) to keep your controllers clean and focussed on their intent.

Finding Freedom in “JavaScript Fatigue”

A lot of people have spoken about “JavaScript fatigue”: the idea that there are so many new frameworks, tools and ideas available to the average JavaScript developer that it’s impossible to keep up. I thought I’d add my opinion.

When I started learning JavaScript it used to be that I would try to keep up with everything. I suspect now that I just didn’t know how much was out there, but it really felt like that was an acheivable target. I would make a real effort to not only read up on new frameworks & libraries but to try them out: maybe a quick tutorial, maybe a few introductory posts, maybe even a small project.

Now, things have changed and it is obvious to most of us that there is no way you can invest that much time in every new thing that comes out.

For me, this is not a bad thing. In fact, I find it pretty liberating.

The whole situation reminds me a little bit of when I first joined Twitter. I was following maybe 20 people and I would make a real effort to read every single tweet. Ridiculous, right? But still I tried. Then I started following more people and then more people and with every extra piece of content it became less and less realistic to get through everything.

So I let go. I had to.

I couldn’t keep up with everything so I stopped trying to do the impossible and learned to let the mass of information wash over me. If something particularly catches my eye then I can read up on it but if I miss something? Who really cares?

Nowadays it feels the same with JavaScript frameworks. I may never have a chance to get my hands dirty with everything that comes out. In fact, I may never even hear of some of them. But I don’t worry any more about trying to keep up and if something really is the next big thing… well, I’m pretty sure I’ll hear about it soon enough.

Cleaning up Resources using MutationObserver

Cleaning up resources?

Let’s say you’ve written a shiny new component in your favorite framework and somewhere along the way you’ve allocated a resource that cannot be automatically cleaned up by the browser.

Maybe you attached an event handler to the resize event on the window.  Maybe you passed a callback to a global object.  Whatever the case, you need to tidy up those resources at some point.

Easy enough, right?  Put a dispose method on our object to clean up it’s leftovers and make sure it’s called before the object is discarded.

Problem solved?

Problem not quite solved

What if, for whatever reason, your component doesn’t have control over the parent?  You could trust that the user will do the right thing and call dispose for you but you can’t guarantee it.

As an alternative, can we automatically clean up our resources as soon as our containing DOM element is removed?

Yes.  Yes we can.

Using MutationObserver

The MutationObserver API (which has pretty great browser support) lets you listen to changes made to a DOM node and react to it.  We can use it here to perform our cleanup.

When we create an instance of MutationObserver we specify a callback that gets details of changes made to the parent.  If those changes include the removal of our target element then we can call dispose.

Here we are observing the parent of our target node, not the node itself (which would not be notified if removed).  We need to specify { childList: true } as the second parameter to be notified of additions and removals of child items.

Disposing the Observer

Finally, we need to make sure that the observer itself doesn’t cause a memory leak!  The observer is connected to the parentElement which (we assume) will still be hanging around, so we need to make sure that we disconnect it as part of disposal.

With everything pulled together the final version looks like this…