简体   繁体   中英

How can I update a React component depending on redux action outcome

I have a React component that contains an input. The component dispatches a redux action that calls an API. I want to display a "SUCCESS" message per component but I can't work out how to get the message, other than through the reducer? But if I was to do it through the reducer it would just be a general message that would update all the form components?

//COMPONENT

export default class Stock extends React.Component {


constructor(props) {
    super(props);

    this.state = {
        value: "",
        id: this.props.hit['Name of Item'],
    }
  }

handleChange(e){
    e.preventDefault();
    this.setState({value: e.target.value});
}

handleClick(e){
    e.preventDefault();
    this.props.dispatch(updatePosStock(this.state.value, this.state.id));
}

render() {

    return (
            <div className="item">
                <p>{this.props.hit['Name of Item']}: {this.props.hit.Quantity}</p>
                <p>Stock ID: {this.props.hit.objectID}</p>
                <div className="control">
                <input className="input" value={this.state.value} type="text" onChange={this.handleChange.bind(this)} />
                <a href="" onClick={this.handleClick.bind(this)} >save</a>

                //MESSAGE TO DISPLAY HERE DEPENDING ON handleClick

                </div>
            </div>
        )
    }
}

//ACTION

export function updatePosStock(product, stock){
return function(dispatch) {
    axios.post('/api/v1/product/', {
        product,
        stock
    })
    .then((response) => {
        //console.log(response.data);
        return response.data;
        //dispatch({type: 'FETCH_PRODUCT_FULFILLED', payload: response.data})
    })
    .catch((error) => {
        console.log(error);
    })
}
}

If I understand, this component is rendered multiple times on the page and when you trigger action on one, message gets distributed to all the components (because it is not distinguishable between components.

So, I'd set message to be something like:

[{
  message: 'Success',
  item: itemId
},
...]

and when sending a data to api I'd send for which item it is being sent and would just push this message to message array, then in place where you want to display message you put

{this.displayMessage(messagesVar, index) || ''}

So when display message is not falsy it displays message.

messageVar is what your reducer updates.

Display message function is smt like

(messages, id) => { 
     currentComponentMsgs = messages.filter((item) => item.id === id);
     return currentComponentMsgs.length > 0 
            ? currentComponentMsgs.mat((item) => item.message).reduce((a,b) => a + ', ' + b}
            : '');
  }

I did not have time to check if it all works, but if you propagate id of each elemnt properly, using this pattern you can even have multiple messages recorder in messagesVar, so it serves as kind of a mailbox for components. You can use it on all of your components.

There are two ways you can achieve this

1. save your response message (eg 'success') in the form of an array

In this case you can define a state such as feedback: [], for example. Then you will set the message this way

...
//dispatch({type: 'FETCH_PRODUCT_FULFILLED', payload: [ product : response.data]})
..

Then in your component you do the following

...
<input className="input" value={this.state.value} type="text" onChange={this.handleChange.bind(this)} />
<a href="" onClick={this.handleClick.bind(this)} >save</a>

//MESSAGE TO DISPLAY HERE DEPENDING ON handleClick
<p className="feedback">{this.props.feedback[this.state.value] || ''}</p>

Depending on how you import your states in to your components.

2. Create another property in your redux state or inside this.state={} for example inputKey . Then set inputKey value through handleClick(e, inputKeyValue) You could pass inputKey as a third parameter to the function dispatched and continue with display as should above in you component.

I strongly recommend that you handle all your states with redux and avoid using this.state and this.setState() as much as you can.

I hope it helps.

Use callback for your action:

handleClick(e){
    e.preventDefault();
    this.props.dispatch(updatePosStock(this.state.value, this.state.id, 
    (code, message)=>{if (code==='OK')this.setState({displayMessage:true, message:"Successfully saved"})}));
}

///ACTION
export function updatePosStock(product, stock, callback){
return function(dispatch) {
    axios.post('/api/v1/product/', {
        product,
        stock
    })
    .then((response) => {
        callback('OK')
        return response.data;
        //dispatch({type: 'FETCH_PRODUCT_FULFILLED', payload: response.data})
    }) .catch((error) => { callback(error.code, error.message)
        console.log(error);
    })
}
}

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