简体   繁体   中英

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?

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

First, did you try to use arrow function in handleClick?

handleClick() {
    fetch('api').then(() => {
        this.setState({ clicked: true });
        this._alert.show('Cool response!');
    });
}

That may affect the context of this

On the other hand, you should create two actions if you use Redux. Such as, fetchAPI() and APIfetched() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM