When implementing a state container like Redux or MobX, your state and events are usually moved to a separate class or object that no longer can read the refs.
For example, in a normal component:
import Alert from Alert.js;
class Dummy extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false }
}
handleClick() {
fetch('api').then(function(){
this.setState({ clicked: true });
this._alert.show('Cool response!');
});
}
render() {
return (
<div>
<Alert ref={a => this._alert = a} />
<Button onClick={this.handleClick}/>
</div>
)
}
}
If I click the button, once the server request is completed, the state is updated and the alert is triggered. Using refs like this is quite common in some modal and alert libraries.
Now, in Redux (or any Flux implementation), that fetch() will live in an action, which lives in a separate file, which doesn't have access to this._alert
.
What would be the best way to maintain the functionality without rewriting the external "Alert" library?
As a note, I came from your post: https://dannyherran.com/2017/03/react-redux-mobx-takeaways/
This is wrong from the start. Refs should not be shared between components because that couples them. Components should be designed completely decoupled from one another and react based on state given to them.
The problem is that the ref tells you nothing about the state of the component itself, you don't know if it mounted, you don't know if it even exists, so you're playing with something volatile.
So let's decouple everything and leverage the power of react/redux properly, all code should be organized in this manner:
Reducer will maintain the display state of the alert. Any component in your entire application now has access to it, independent of refs, so it won't matter if the component actually exists or not.
const DummyPageReducer = function(state, action)
{
if (state === undefined)
{
state =
{
alertShow: false
};
}
switch(action.type)
{
case 'ALERT_DISPLAY':
return Object.assign({}, state, { alertShow: action.display });
}
return state;
}
Action to adjust the display setting of the alert, and async action to perform the fetch and produce the correct result.
export const ALERT_DISPLAY = 'ALERT_DISPLAY '
export function alertDisplay(display)
{
return {
type: ALERT_DISPLAY,
display
}
}
export function showAlert()
{
return function (dispatch)
{
fetch('api').then(function()
{
dispatch(alertDisplay(true))
});
}
}
The connected component. No need to share refs, the ref will be used, but the component will react to its given props and set the Alert accordingly.
import Alert from Alert.js;
class Dummy extends React.Component
{
constructor(props)
{
super(props);
this.setAlert = this.setAlert.bind(this);
}
setAlert()
{
if(this.props.alertShow)
this._alert.show('Cool response!');
else
this._alert.hide();
}
componenDidMount()
{
setAlert();
}
componentDidUpdate(prevProps, prevState)
{
if(prevProps.alertShow !== this.props.alertShow)
setAlert();
}
render()
{
return
(
<div>
<Alert ref={a => this._alert = a} />
<Button onClick={this.props.showAlert}/>
</div>
)
}
}
Dummy = connect(
state =>
({
alertShow: state.Dummy.alertShow
}),
dispatch =>
({
showAlert: () => dispatch(showAlert(true))
})
)(Dummy);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With