Skip to content

Instantly share code, notes, and snippets.

@bminer
Last active October 11, 2018 20:17
Show Gist options
  • Select an option

  • Save bminer/e9e6f06d80d0de0792cd026d68d9b7ec to your computer and use it in GitHub Desktop.

Select an option

Save bminer/e9e6f06d80d0de0792cd026d68d9b7ec to your computer and use it in GitHub Desktop.

Revisions

  1. bminer revised this gist Oct 11, 2018. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions reactHandlerUtils.js
    Original file line number Diff line number Diff line change
    @@ -22,7 +22,7 @@ is equivalent to:
    a: {
    ...comp.state.a,
    b: {
    ...comp.state.b,
    ...comp.state.a.b,
    c: value
    }
    }
    @@ -47,7 +47,7 @@ are all equivalent to:
    a: {
    ...comp.state.a,
    b: {
    ...comp.state.b,
    ...comp.state.a.b,
    ...value
    }
    }
    @@ -126,9 +126,9 @@ This is roughly equivalent to this monstrosity:
    a: {
    ...comp.state.a,
    b: {
    ...comp.state.b,
    ...comp.state.a.b,
    c: {
    ...comp.state.c,
    ...comp.state.a.b.c,
    d: e.target.value
    }
    }
  2. bminer revised this gist Oct 9, 2018. 1 changed file with 0 additions and 11 deletions.
    11 changes: 0 additions & 11 deletions reactHandlerUtils.js
    Original file line number Diff line number Diff line change
    @@ -165,14 +165,3 @@ export function updateKeyEvent(component, key, opts) {
    return e => update(e.target.value);
    }
    }

    // Convert camelCase or PascalCase to proper case
    export function camelToProper(str) {
    return (
    str
    // insert a space before all caps
    .replace(/([A-Z])/g, " $1")
    // uppercase the first character
    .replace(/^./, str => str.toUpperCase())
    );
    }
  3. bminer revised this gist Oct 9, 2018. 1 changed file with 16 additions and 3 deletions.
    19 changes: 16 additions & 3 deletions reactHandlerUtils.js
    Original file line number Diff line number Diff line change
    @@ -76,17 +76,19 @@ export function updateKey(component, key, opts) {
    // Deep clone everything along the path to make React happy and
    // avoid mutating `this.state` directly
    for (i = 0; i < path.length - 1; i++) {
    obj[path[i]] = Object.assign({}, state[path[i]]);
    const target = state[path[i]] instanceof Array ? [] : {};
    obj[path[i]] = Object.assign(target, state[path[i]]);
    // Go deeper into `obj` and `state`
    state = state[path[i]];
    state = state[path[i]] || {}; // Note: use {} as fail-safe
    obj = obj[path[i]];
    }
    // Update the value on the deeply cloned Object
    // There are 2 options: merge Object state or replace state
    if (merge === undefined) merge = opts.merge;
    if (merge && typeof value === "object") {
    // Merge state
    obj[path[i]] = Object.assign({}, state[path[i]], value);
    const target = state[path[i]] instanceof Array ? [] : {};
    obj[path[i]] = Object.assign(target, state[path[i]], value);
    } else {
    // Replace state
    obj[path[i]] = value;
    @@ -163,3 +165,14 @@ export function updateKeyEvent(component, key, opts) {
    return e => update(e.target.value);
    }
    }

    // Convert camelCase or PascalCase to proper case
    export function camelToProper(str) {
    return (
    str
    // insert a space before all caps
    .replace(/([A-Z])/g, " $1")
    // uppercase the first character
    .replace(/^./, str => str.toUpperCase())
    );
    }
  4. bminer created this gist Oct 4, 2018.
    165 changes: 165 additions & 0 deletions reactHandlerUtils.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,165 @@
    /* Returns a function `(value, merge) => {...}` that calls
    `component.setState(...)` to update the component's state with the new value
    for `key`.
    A simple example:
    const func = updateKey(component, "counter");
    func(23);
    which is equivalent to:
    component.setState({counter: 23});
    `key` can be a nested path into an Object delimited by `.` For example:
    updateKey(comp, "a.b.c")(value)
    is equivalent to:
    comp.setState({
    a: {
    ...comp.state.a,
    b: {
    ...comp.state.b,
    c: value
    }
    }
    })
    Note that `a` and `b` are shallowly cloned to prevent direct mutations to
    `comp.state`. One main purpose of this function is to eliminate a lot of
    bookkeeping associated with nested object cloning.
    There is also a `merge` argument that allows the `value` to be merged into the
    Object located at `key`. For example:
    updateKey(comp, "a.b")(value, true) // explicitly merge
    updateKey(comp, "a.b", {merge: true})(value) // merge by default
    updateKey(comp, "a.b", {merge: false})(value, true) // merge; ignore default
    are all equivalent to:
    comp.setState({
    a: {
    ...comp.state.a,
    b: {
    ...comp.state.b,
    ...value
    }
    }
    })
    `opts` can be passed to `updateKey` to change some behavior:
    - `merge` - Sets the default `merge` value for the returned function
    - `cb` - Callback to be passed to `setState` and called after setting state.
    */
    export function updateKey(component, key, opts) {
    opts = opts || {};
    // Return a function that calls `setState` with custom updater and `cb`
    return (value, merge) =>
    component.setState((state, props) => {
    // Split the `key` using "." as delimiter to determine path
    const path = key.split(".");

    // Create a `newState` object to be returned
    const newState = {};

    // `state` will point to the current Object in `state`
    // `obj` will point to the current Object in `newState`
    let obj = newState;
    // `i` will keep track of how deep the Object `path` is
    let i;
    // Deep clone everything along the path to make React happy and
    // avoid mutating `this.state` directly
    for (i = 0; i < path.length - 1; i++) {
    obj[path[i]] = Object.assign({}, state[path[i]]);
    // Go deeper into `obj` and `state`
    state = state[path[i]];
    obj = obj[path[i]];
    }
    // Update the value on the deeply cloned Object
    // There are 2 options: merge Object state or replace state
    if (merge === undefined) merge = opts.merge;
    if (merge && typeof value === "object") {
    // Merge state
    obj[path[i]] = Object.assign({}, state[path[i]], value);
    } else {
    // Replace state
    obj[path[i]] = value;
    }
    return newState;
    }, opts.cb);
    }

    /* Returns a function `(e) => {...}` that calls `component.setState(...)` to
    update the component's state with the new value for `key` using
    `e.target.value` as the value. See `updateKey` above for more details.
    If `e.target` has a `name` property, `{[e.target.name]: e.target.value}` will
    be merged into `key` instead of replacing the value at `key` with
    `e.target.value`.
    Usage in React `render()` function:
    <input onChange={updateKeyEvent(this, "counter")} />
    In the above code, when the <input> is changed, the state's "counter" key will
    be updated to match the value of the <input>.
    Another example:
    <input
    name="d"
    onChange={updateKeyEvent(this, "a.b.c")}
    />
    This is roughly equivalent to this monstrosity:
    <input
    onChange={(e) =>
    this.setState({
    a: {
    ...comp.state.a,
    b: {
    ...comp.state.b,
    c: {
    ...comp.state.c,
    d: e.target.value
    }
    }
    }
    })
    }
    />
    In the above code, when the <input> is changed, `this.setState(...)` will be
    called such that `this.state.a.b.c` is merged with `{d: value}` where `value`
    matches the current value of the <input>.
    `opts` can be passed to change some behavior:
    - `preventDefault` - By default, `e.preventDefault()` is called. To prevent
    this call, set `preventDefault` explicitly to `false`
    ... All other `opts` for `updateKey`
    */
    export function updateKeyEvent(component, key, opts) {
    opts = opts || {};
    const update = updateKey(component, key, opts);
    if (opts.preventDefault !== false) {
    // Call `preventDefault` by default unless explicitly set otherwise
    return e => {
    e.preventDefault();
    const { name, value } = e.target;
    if (name) {
    // Special case: merge Object into `key`
    return update({ [name]: value }, true);
    } else {
    return update(value);
    }
    };
    } else {
    return e => update(e.target.value);
    }
    }