Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: How to access component refs from Redux/Flux actions?

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?

like image 372
Dan H Avatar asked Dec 11 '22 12:12

Dan H


1 Answers

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:

1. Reducer:

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;
}

2. Action and Async Action:

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))
        });

    }

}

3. Connected Component:

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);
like image 155
Steven Avatar answered Jan 31 '23 08:01

Steven