react-calendar vs. MobX

Wir verwenden den react-calender (3.4.0) im Zusammenspiel mit MobX (6.3.1) / mobx-react (7.2.0) und nach langem erfolgreichen Einsatz gibt es eine Kleinigkeit, die etwas überrascht hat. Hier eine kurze Beschreibung der Hintergründe und meine aktuelle Lösungsidee – nicht sehr elegant, aber aktuell einfach ein Nebenschauplatz. Das storybook als Beispiel habe ich unten angehängt (TypeScript).

Started man die Story, so wird in jede Zelle des Kalenders ein zusätzlicher Text eingeblendet.

Ändert man allerdings im Eingabefeld den Wert, so tut sich im Kalender nichts.
Wie kann das sein? Ich gebe hier mal meine Interpretation ab:
  • Im Beispiel (wie auch im realen Projekt) werden die Daten als observable Objekt (data) durchgereicht.
  • Für die Eingabe (InputTest im Beispiel) funktioniert das auch wie gewünscht / immer.
  • Der eigentlich wichtige Komponente (CalendarTest) ignoriert das aber.

Warum könnte das sein? Nun tatsächlich ist es ja der entscheidende Gag von MobX dass sich in der Tat data gar nicht ändert, sondern immer ein identischer Proxy auf die eigentlichen Daten ist. Erst der Einsatz des observer Dekorators sorgt dafür, dass Datenzugriffe in der render Methode vermerkt werden und Änderungen sehr detailliert zu Aktualisierungen führen. Aber hier passiert nichts, obwohl doch “offensichtlich” auf props.data.text zugegriffen wird, um den zusätzliche Inhalt anzuzeigen.

Mein aktueller Kenntnisstand dazu: die Kalender Komponente erzeugt die Zellen (und damit auch den tileContent) nicht im render, sondern im static getDerivedStateFromProps – und tatsächlich haben sich aus Sicht von React die props ja gar nicht verändert! Ich vermute es handelt sich hier um eine Optimierung, die auch fast immer greift – bis so High Tech Lösungen via MobX eine Änderungsüberwachung jenseits von React einbringen.

Ich habe mich für den Moment entschieden, einen Work-Around zu verwenden – eine für fast alle Anwender von react-calendar traurige Lösung wäre auf das getDerivedStateFromProps zu verzichten, eine andere im MobX durch den observer auch getDerivedStateFromProps überwachen zu lassen, da gibt es wohl schon ähnliche Diskussionen in den Issues.

Als Lösung gibt man der Kalender Komponente einfach eine zusätzliche key Eigenschaft, die sich immer verändert, wenn die Zellen neu erstellt werden sollen – natürlich nicht wie hier manuell über eine Schaltfläche sondern elegant über eine MobX computed Eigenschaft, aber das Detail erspare ich mir hier einfach, jeder der MobX verwendet weiß sicher, was ich meine.

Drückt man in der Story die Schaltfläche, so wird über die veränderte Eigenschaft die Kalender Komponte gezwungen, ihr getDerivedStateFromProps neu aufzubauen – et voilà.

Have Fun!

Jochen

import { withKnobs } from '@storybook/addon-knobs'
import { observable } from 'mobx'
import { observer } from 'mobx-react'
import * as React from 'react'
import Calendar from 'react-calendar'

export default { decorators: [withKnobs], title: 'Fehler et al/Kalendar Tile Aktualisierung' }

interface IEntity {
    text: string
}

interface ICalendarTestProps {
    data: IEntity
    key: number
}

const CalendarTest = observer(
    (props: ICalendarTestProps): JSX.Element => (
        <Calendar
            key={props.key}
            closeCalendar={false}
            tileContent={() => <div>{props.data.text}</div>}
            value={null}
            view='month'
        />
    )
)

interface IInputTestTestProps {
    data: IEntity
}

const InputTest = observer(
    (props: IInputTestTestProps): JSX.Element => (
        <input type='text' value={props.data.text} onChange={(ev) => (props.data.text = ev.target.value)} />
    )
)

export const standard = ((): React.ReactNode => {
    const data = React.useMemo<IEntity>(() => observable({ text: 'C' }), [])
    const [key, setKey] = React.useState(0)

    return (
        <div>
            <button onClick={() => setKey(key + 1)}>UPDATE KEY</button>
            <InputTest data={data} />
            <CalendarTest key={key} data={data} />
        </div>
    )
}) as React.FC

Nützlich lernen…

Ausgehen von einem echten Bedarf habe ich mal wieder ein kleines Lernprojekt gestartet.

Gebraucht wurde ein kleines Werkzeug (eigentlich nur unter Windows), das in einem Dateibaum identisches Dateien (konkret Bilder) erkennt und bei der Bereinigung von Duplikaten unterstützt (auch einen eventuell unerfahrenen Anwender).

In verschiedenen Varianten (oft kostenpflichtig, zumeist als umfangreiches All-In-One Tool) gibt es so etwas schon, daher um kein zu schlechtes Gewissen zu bekommen habe ich daraus dann direkt ein Lernprojekt gemacht. Zum Beispiel:

  • das soll unter Windows und Linux laufen, daher habe ich mir electron als Entwicklungsplattform ausgesucht.
  • electron mit WebPack, CSS Modulen u.a. hatte ich noch nie probiert.
  • Ein Deployment mit dem electron Builder ist auch ganz spannend.
  • uns sonstiger Kleinkram…

Herausgekommen ist dann das hier: https://github.com/JMS-1/file-organizer – befindet sich noch in der Testphase und könnte sicher auch etwas optische Aufbereitung (e.g. via semantic-ui) brauchen, aber das ist nicht das Ziel gewesen.

Viel Spaß

Jochen

Und noch eine kleine App

Ergänzend zu BitMe habe ich noch ein kleines Spielchen im Umfeld der binären Zahlen entwickelt – passend zum Informatikunterricht zu Beginn der Mittelstufe. Bei BoolIt ist es die Aufgabe, für die einfachen Logikgatter auf Basis von zufällig gewählten Eingängen so schnell wie möglich den Wert des Ausgangs zu bestimmen.

Der Quellcode ist wie gewohnt auf GitHub zu finden. Auch diese App ist wieder bewusst einfach gehalten und tatsächlich habe ich im Wesentlichen die selben Android Basistechnologien wie bei BitMe verwendet – also nicht wirklich viel Neues gelernt. Ich denke das nächste Projekt muss zumindest ein noch weißes Feld enthalten – schauen wir mal!

Frohe Ostern

Jochen

BitMe BoolIt IEC Unterstützung

Jeder hat mal klein angefangen…

Um einmal ein wenig aus den tief eingelaufenen (Microsoft) Wegen zu entfliehen und vielleicht auch mal einen Blick in die persönliche Zukunft zu werfen, habe ich mich entschlossen ein bisschen (wenn auch private) praktische Erfahrungen im Mobile Umfeld zu sammeln. Nach einigen Enttäuschungen bei Versuchen mit einer Web App (auf Basis von jQuery Mobile) – über die ich vielleicht auch einmal hier berichten werde – und dem bewussten Überspringen von Xamarin (bedingt durch ein ziemlich schlechtes Buch) habe ich als zweiten Versuch direkt das Android SDK mit dem Android Studio verwendet. Lustiger Weise konnte ich hier zig Jahre alte Java Kenntnisse direkt nutzen – irgendwie trotzt einiger Neuerung doch alles sehr vertraut.

Naja, so richtig toll ist die erste App nicht geworden, vor allen was die graphische Gestaltung angeht. Aber sie scheint ihren Zweck zu erfüllen (zum Hintergrund: mein Sohn muss sich in Informatik gerade mit dem Binärsystem auseinandersetzen), daher habe ich sie einfach mal auf Google Play veröffentlicht. Den Quellcode gibt es natürlich auch wie gewohnt auf GitHub, aber da ich selbst noch ganz am Anfang stehe kann man hier nicht viel lernen.

Mein Fazit: nach einigen Einstiegsschwierigkeit ist das Android Studio doch eine recht angenehme IDE. Die Grundstruktur einer Android App ist durchaus vergleichbar mit anderen UI Konzepten, die mir im Laufe der Jahre so begegnet sind – in meinen Augen vor allem WPF und Silverlight. Allerdings glaube ich, dass eine App für alle Formfaktoren wirklich kein Kinderkram ist – ich habe alleine schon Mühe gehabt, eine einfach Seite auf einem Galaxy S4 auch auf einem S3 Mini einigermaßen bedienbar zu bekommen.

Aber ich lerne ja noch 🙂 Und es macht wirklich Spaß, mal wieder Neuland zu betreten – trotz der immer wiederkehrenden kleinen und großen Rückschläge.

In diesem Sinne: Death to the Comfort Zone!

Jochen