Add mobx to your React project

Mobx helps you manage the state.

I set up mobx to be used as an import to your (functional) components. I do this, rather than using createContext() because the latter one only allows you to use it inside components, not functions.

To set up mobx you’ll need to:

  1. install mobx
  2. create store
  3. configure store and components

In this example, I make use of the function autoStore() in the constructor of the store to save the state to localStorage whenever there is a change to the state.
This makes that state is saved across page-refreshes.

install mobx

Install with:

yarn add mobx mobx-react

create store

Create your store by creating file src/stores/uiStore.tsx.

This will be one of the stores you can use. You can create as many stores as you want.

// file src/stores/uiStore.tsx
import {makeAutoObservable} from "mobx";

const uiStore = () => makeAutoObservable({
    showLocations: false as boolean,
        this.showLocations = ! this.showLocations

export default uiStore

Next, we add this store to the context. This makes that we can access all stores via the useStore context.

// file ./src/stores/index.tsx
import {createContext, useContext} from "react";
import uiStore from "./uiStore";

const store = {
    ui: uiStore(),

export const StoreContext = createContext(store)

export const useStore = () => {
    return useContext<typeof store>(StoreContext)

export default store

In your App.tsx, add the StoreContext so every child component can make use of it.

// file ./src/App.tsx
import store, {StoreContext} from "../stores";

const MyApp = () => {

    return (
      <StoreContext.Provider value={store}>
            <Header />

File ./src/libs/autoStore.js takes care of storing the state in localStorage.

// file ./src/libs/autoStore.tsx
import { toJS, autorun, set } from 'mobx'

export default function (_this: any, storeName='store') {
    let firstRun = true

    // will run on change
    autorun(() => {
        // on load check if there's an existing store on
        // localStorage and extend the store
        if (firstRun) {
            const existingStore = window.localStorage.getItem(storeName)

            if (existingStore) {
                set(_this, JSON.parse(existingStore))

        // from then on serialize and save to localStorage
        const serializedThis = toJS(_this)

    firstRun = false

Use the store: configure store and components

Configure your components by wrapping them with the observer() function of mobx.

By doing this, mobx knows whenever something changes that concerns the state.

// file ./src/components/SayHello.js

import React, { Component } from 'react'
import { observer } from 'mobx-react-lite'
import {useStore} from "../../stores";

const SayHello = () => {
   const {ui} = useStore()

   return <div>{ui.showLocations && 'Hi locations!'}</div>

export default observer(SayHello)


Take a look at these pages. They provide useful information about working with mobx.

Caprover doesn’t set MySQL user or database

The problem: one-click app MySQL won’t create database or user

In caprover, formerly captainduckduck, I had the issue that the database and a user were not created by setting environment variables MYSQL_USER and MYSQL_DATABASE.

While building, this message was displayed by Docker:

[Warning] One or more build-args [MYSQL_USER, MYSQL_DATABASE] were not consumed

So appearently, Caprover does not support setting the environment variables for (only?) MySQL anymore.

Selecting MySQL from the oneclick-apps gives you the following options.
Note the absence of setting extra environment variables.

adding a MySQL one-click-app in Caprover

You can only set the root password.

When you start this new app, only the root password is set. If you now set the environment variables, MySQL will not create the database or user. It does this only on first startup, after creating the one-click-app for MySQL.

Solution: supply Dockerfile with the environment variables set

My workaround? Don’t use a one-click app and supply a Dockerfile directly inside Caprover instead.

This is what I used:

  1. Create a new app (and select ‘has persistent data‘)
  2. Open the newly created app by clicking on its name in the Apps overview
  3. Click tab ‘Deployment
  4. Scroll down to ‘Method 4: Deploy plain Dockerfile
  5. Insert and edit the following content and click on ‘Deploy Now
ENV MYSQL_DATABASE=dbname_for_app
adding Dockerfile contents directly into your app

When you deploy your new database app now, the database and the user will be created.

Help your future self: set environment variables after the fact

TIP: after this, you won’t see the Dockerfile contents anymore. So in the future it might be unclear what values were set.
In order to overcome this, set the environment variables as well over at App Configs > Environmental variables:

add the environment variables in ‘bulk mode’

That’s it! Now let’s hope we’ll be able to set environment variables in the one-click setup some day soon again.

Install Gearman from source on Ubuntu 18.04

I tried to install Gearman on my Ubuntu 18.04 Docker container and encountered a lot of issues.

This write-up is to save you some valuable time of your life.


apt update
apt install -y g++ uuid-dev gperf libboost-all-dev libevent-dev curl 

cd /tmp
curl -LO
tar xvzf gearmand-1.1.18.tar.gz
cd gearmand-1.1.18
./configure --with-boost-libdir=/usr/lib/x86_64-linux-gnu
make test
make install

# Start gearman in the background
gearmand --log-file /var/log/gearmand.log --daemon

To get there, I encountered the following issues:

configure: error: Unable to find libevent

./configure --with-boost-libdir=/usr/lib/x86_64-linux-gnu
checking test for a working libevent... no
configure: error: Unable to find libevent

Fix: apt install -y libevent-dev

configure: error: Could not find a version of the library!

./configure --with-boost-libdir=/usr/lib/x86_64-linux-gnu
checking whether the Boost::Program_Options library is available... yes
configure: error: Could not find a version of the library!

Fix: apt install -y libboost-all-dev

Installing the PHP client libraries for Gearman

If you intend to use PHP to talk to your Gearman service, use the following to get the libraries installed.

Note: you might not need the last line. This is to enable the Gearman PHP extension in Docker.

cd /tmp \
  && wget\
  && tar xvzf gearman-2.0.6.tar.gz \
  && mv pecl-gearman-gearman-2.0.6 pecl-gearman \
  && cd pecl-gearman \
  && phpize \
  && ./configure \
  && make -j$(nproc) \
  && make install \
  && cd / \
  && rm /tmp/gearman-2.0.6.tar.gz \
  && rm -r /tmp/pecl-gearman \
  && docker-php-ext-enable gearman

Bash: use colored text in script

I often use these handy helperfunction to write colored text in my scripts.

# Bash text-colors
NC='\033[0m' # No Color

function echoRed() {
  printf "${RED}${TEXT}${NC}\n"

function echoGreen() {
  printf "${GREEN}${TEXT}${NC}\n"

echoGreen "This text is green"
echoRed "This text is red"
echo "This is regular text"

XPATH: search element by text

When I write selenium/kantu end-to-end test I often need to use xpath to find certain elements.
And while it’s definitely not the fastest, searching elements by the text in it, is easy and fast to use. It’s also more descriptive to an untrained eye.

I often refer to this post on StackOverflow which has it written down quite neatly:

It basically comes down to this:

Literal match (= must match exactly and completely):


This matches: elements containing (just) the text ‘match’ and a button with (just) the text ‘Save’

Do you need to strip whitespace from texts in elements before you do the matching, then use this:


Partly matching:

If it’s ok to be not so strict, use this more loosely matching:


This matches texts like: ‘this match comes up‘ and ‘matches this’

If your text must match with words beginning with the text or lines starting or ending with that text, use this xpath 2.0 expression:


Search element with specific class

//*[contains(@class, 'Test')]

Get parent element

//*[contains(@class, 'Test')]/parent::*
//*[contains(@class, 'Test')]/..

Multiple conditions

NOTE: that you MUST use single quotes and the word ‘and’ MUST be lowercase.

//*[contains(@class, 'Test') and contains(text(), 'match this text')]

//button[@id='add-user' and contains(@class, 'active')]

PRO-tip: use the inspector to test your xpath.

open the inspector and search (<C-f> or <Command+F>)

Gulp error: Did you forget to signal async completion?

First of all: do check the Gulp documentation on this:

I had the following gulpfile.js:

# file gulpfile.js
function build() {
    return series(
} = build;

When running gulp build I got the following errors:

$ gulp build
[11:17:33] Using gulpfile ./gulpfile.js
[11:17:46] The following tasks did not complete: build
[11:17:46] Did you forget to signal async completion?


I fixed it by making the build() function async: async build().
Then my gulpfile.js looked like the following (note the extra parentheses at the end!)

# file gulpfile.js
async function build() {
    return series(
} = build;

Dataverlies voorkomen in Google Analytics

Wist je dat filters in Google Analytics destructieve features zijn? Dat wil zeggen, als je filter meer filtert dan je zou willen, dan ben je die data kwijt. Dat kun je voorkomen door meerdere views te maken in Google Analytics.

Want je wilt geen data kwijt zijn.

Zorg er daarom voor dat je altijd een view hebt zonder filters. Want filters zijn gekoppeld aan een view. Heb je geen filters op je view, dan wordt netjes _alle_ data bewaard.

Continue reading
1 3 4 5 6 7 9