Last active
December 26, 2022 07:05
-
-
Save idiotWu/099161ef9841029c6189ab27519c2d3b to your computer and use it in GitHub Desktop.
Hybrid controlled components solution: https://idiotwu.me/react-hybrid-controlled-components-in-action/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Optimize hybrid controlled component by add some method into proto | |
* | |
* Usage: | |
* @hybridCtrl | |
* class App extends React.Component { | |
* ... | |
* } | |
* | |
* @hybridCtrl('specified_prop_to_assign') | |
* class App extends React.Component { | |
* ... | |
* } | |
* | |
* @hybridCtrl('specified_prop_to_assign', '_internal_prop') | |
* class App extends React.Component { | |
* ... | |
* } | |
*/ | |
import shallowCompare from 'react-addons-shallow-compare'; | |
const noop = () => {}; | |
const optimizer = (Component, key = 'value', internalKey = `_${key}`) => { | |
// need `this` | |
function shallowCompareWithExcept(nextProps, nextState) { | |
const props = { | |
...nextProps, | |
[key]: this.props[key], // patched with same value | |
}; | |
const state = { | |
...nextState, | |
[internalKey]: this.state[internalKey], | |
}; | |
return shallowCompare(this, props, state); | |
} | |
const { | |
shouldComponentUpdate = shallowCompareWithExcept, | |
componentWillReceiveProps = noop, | |
} = Component.prototype; | |
Object.defineProperty(Component.prototype, 'displayValue', { | |
get: function getDisplayValue() { | |
// prefer to use `props[key]` | |
return this.props[key] !== undefined ? | |
this.props[key] : this.state[internalKey]; | |
}, | |
}); | |
// assign new props to state | |
Object.defineProperty(Component.prototype, 'componentWillReceiveProps', { | |
configurable: false, | |
enumerable: false, | |
writable: true, | |
value: function componentWillReceivePropsWrapped(nextProps) { | |
const controlledValue = nextProps[key]; | |
if (controlledValue !== undefined && | |
controlledValue !== this.state[internalKey] | |
) { | |
this.setState({ | |
[internalKey]: this.mapPropToState ? | |
this.mapPropToState(controlledValue) : controlledValue, | |
}); | |
} | |
componentWillReceiveProps.call(this, nextProps); | |
}, | |
}); | |
// patch shouldComponentUpdate | |
Object.defineProperty(Component.prototype, 'shouldComponentUpdate', { | |
configurable: false, | |
enumerable: false, | |
writable: true, | |
value: function shouldComponentUpdateWrapped(nextProps, nextState) { | |
let result = true; | |
if (nextProps[key] !== undefined) { | |
// controlled, use `props[key]` | |
result &= (nextProps[key] !== this.props[key]); | |
} else { | |
// uncontrolled, use `state[internalKey]` | |
result &= (nextState[internalKey] !== this.state[internalKey]); | |
} | |
// logic OR rocks | |
return result || | |
shouldComponentUpdate.call(this, nextProps, nextState); | |
}, | |
}); | |
return Component; | |
}; | |
export const hybridCtrl = (keyOrComp, internalKey) => { | |
if (typeof keyOrComp === 'function') { | |
return optimizer(keyOrComp); | |
} | |
return (Component) => optimizer(Component, keyOrComp, internalKey); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment