|
/** |
|
* Sometimes you need to create a component of which all instances should rely on the same data pool. |
|
* Lets for example consider a country selector dropdown. No matter how much of them you use inside your |
|
* application - the all should share the same list of countries. But passing that list down to each and |
|
* every component instance via props is very tiresome and bloats your app. |
|
* |
|
* Behold: the shared value component pattern! |
|
*/ |
|
import React from 'react'; |
|
import PropTypes from 'prop-types'; |
|
|
|
import Dropdown from 'myComponents/Dropdown'; |
|
|
|
// This country list will be used by ALL selectors in their render function. |
|
let countryList = []; |
|
|
|
// This is the list of update functions where each instance of the |
|
// country list component will attach itself to. |
|
const updaters = []; |
|
|
|
const propTypes = { |
|
/** The country value to be selected */ |
|
value: PropTypes.string, |
|
/** An update function to pass a new country value to the application */ |
|
onChange: PropTypes.func, |
|
}; |
|
|
|
const defaultProps = { |
|
value: null, |
|
onChange: null, |
|
}; |
|
|
|
export default class CountrySelector extends React.Component{ |
|
constructor(props){ |
|
super(props); |
|
// We are binding this, so it can be called from anywhere. |
|
this.boundUpdate = this.forceUpdate.bind(this); |
|
} |
|
|
|
/** |
|
* We put the bound update func to the instances array. |
|
* We only do this after mounting to avoid errors. |
|
*/ |
|
componentDidMount(){ |
|
updaters.push(this.boundUpdate); |
|
} |
|
|
|
/** |
|
* If the component is detached from the DOM, we can also remove the |
|
* update hook. |
|
*/ |
|
componentWillUnmount(){ |
|
const index = updaters.indexOf(this.boundUpdate); |
|
updaters.splice(index, 1); |
|
} |
|
|
|
render(){ |
|
const { |
|
value, |
|
onChange |
|
} = this.props; |
|
|
|
return ( |
|
<Dropdown |
|
list={countryList} |
|
value={value} |
|
onChange={onChange} |
|
/> |
|
); |
|
} |
|
} |
|
|
|
CountrySelector.propTypes = propTypes; |
|
CountrySelector.defaultProps = defaultProps; |
|
|
|
/** |
|
* This will replace the countryList and will cause all instances of the |
|
* country selector dropdown to re-render! |
|
*/ |
|
CountrySelector.updateCountryList = (newCountryList) => { |
|
countryList = newCountryList; |
|
updaters.forEach(cb => cb()); |
|
}; |
|
|
|
// ======================================================================================================= |
|
|
|
/** |
|
* So from anywhere in your application - for example in a REST call, you may now retrieve |
|
* a new country list and propagate it to all instances of the component! |
|
*/ |
|
|
|
fetch('myCountryList.json').then(CountrySelector.updateList); |