Skip to content

Instantly share code, notes, and snippets.

@thenikso
Last active May 29, 2017 10:49
Show Gist options
  • Save thenikso/ccfebcf5f82b789531fa9b1fb5de73f5 to your computer and use it in GitHub Desktop.
Save thenikso/ccfebcf5f82b789531fa9b1fb5de73f5 to your computer and use it in GitHub Desktop.
// A Mobx friendly utility to ease the late dereference of
// objects' observable properties.
// Using Ramda here but you can use your own version of these functions
import { path, init, last, mapObjIndexed } from 'ramda';
// This is the supporting class, see `late` and `deref` later for actual use
class LateDeref {
constructor(obj, field) {
this.obj = obj;
this.fieldPath = field.split('.');
}
deref(differentField) {
const fieldPath = differentField
? differentField.split('.')
: this.fieldPath;
return path(fieldPath, this.obj);
}
// If you have a LateDeref object you can late-assign it a value
// doing ie: `myLateDeref.set(true)`
set(value, differentField) {
const fieldPath = differentField
? differentField.split('.')
: this.fieldPath;
const partialDeref = path(init(fieldPath), this.obj);
partialDeref[last(fieldPath)] = value;
}
toString() {
return `${this.obj}.${this.fieldPath.join('.')}`;
}
}
// With mobx it is best to dereference an observable as late as possible.
// If a component supports a late dereference property (see `deref`), one
// can set that property with `late` to dereferene the observable property
// as late as possible.
//
// Supposing that `DisplayName` uses `deref` for its `name` property, instead
// of doing:
// <DisplayName name={person.name} />
// Prefer:
// <DisplayName name={late(person, 'name')} />
//
// Note that the field might also be a dot separated path like
// `late(person, 'address.street')`.
export function late(obj, field) {
return new LateDeref(obj, field);
}
// Identify a `late` property
export function isLate(prop) {
return prop instanceof LateDeref;
}
// Use `deref` to dereferene a property that might have a `late` value.
//
// const DisplayName = observer(({ name }) => <div>{deref(name)}</div>)
export function deref(prop) {
if (isLate(prop)) {
return prop.deref();
}
return prop;
}
// Derefs a props object. The deref will only work through the first level
// without going deeper.
//
// <MyComp {...derefProps(props)} />
export function derefProps(props) {
return mapObjIndexed(deref, props);
}
@thenikso
Copy link
Author

To clarify, in order to adopt this Mobx/React best practice I found myself creating some weird looking props like loadingOf/loadingField to then do a prop.loadingOf[prop.loadingField] inside the component. With this late/deref utility I can write a normal looking property (which also act like one for the user) say loading and pass it either a bool or a late(this, 'isLoading').

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment