ReactJS Redux异步操作

huangapple go评论78阅读模式
英文:

ReactJS Redux Asynchronous Actions

问题

I have read through lots of tutorial about using redux in reactjs but there are still few things which i am missing. Below is my action method

export const uploadFile = (files) => async (dispatch) => {
    dispatch({ 
        type: T.UPLOAD_REQUEST,
        payload:{UploadStatus:"InProcess"}
     });
    axios.post(`${config.API_URL}/upload`, files)
        .then(res => {
            dispatch({ 
                type: T.UPLOAD_SUCCESS, 
                payload: { UploadResponse: res.data,UploadStatus:"Completed"} 
            });
        }).then(res=>{
            dispatch({ 
                type: T.UPLOAD_SUCCESS, 
                payload: { UploadResponse: null,UploadStatus:"Queued"} 
            });
        })
        .catch(err =>{
            console.log(err);
            dispatch({ 
                type: T.UPLOAD_FAIL, 
                payload: { UploadResponse: err,UploadStatus:"Error" } 
            });
        }
        ).then(res=>{
            dispatch({ 
                type: T.UPLOAD_FAIL, 
                payload: { UploadResponse: null,UploadStatus:"Queued"} 
            });
        })
};

this action uploads file on server and dispatches to reducer to make updates on UI. "UploadResponse" and "UploadStatus" are two state fields which i need to render the UI on frontend.

when request starts I set {UploadStatus:"InProcess"} to render a spinner. After the requests succeeds or fails i set this status fields to "Completed" or "Error" accordingly. this helps me to remove the spinner and render success or fail message.

when request succeeds and UploadStatus is set to "Completed" and a success message pops up on front end and after that user interacts further on component to re render the component, lets say I enter some text in textbox, what happens is UploadStatus still holds the value "Completed" and the message pops up again.

To solve this issue, i make 2 dispatches on each success or fail request response. e.g. you can see in above code, on call success i first dispatched {UploadStatus:"Completed"} , and then {UploadStatus:"Queued"} to reset the status. it helps me in a way that on first dispatch i render the success message on screen, then i reset the Upload Status so that the message to not popup on further interactions on component.

But I have to render a div on frontend when UploadStatus is set to completed, and this div has to be there even after further actions on component. On my second dispatch the UploadStatus is set to "Queued" so the div has disappeared.

I will further add my component code below to explain my logic to render the UI

render() {
  return (
  <div>
   <div className="upload-sect">
   {
      this.props.UploadStatus=="InProcess"?<Spin/>: <></>
      this.props.UploadStatus=="Completed"? 
      this.rendermessage("success","uploded"):<></>
      this.props.UploadStatus=="Error"?this.rendermessage("error","Upload 
      Fail"):<></>
   }
  </div>
  <div>
    {
      this.props.UploadStatus=="Completed"?
      this.renderUploadResponse(this.props.UploadResponse)
      :
      <></>
    }
</div>
</div>)}

Below is my render message function

//message is ANTD design control , which pops up message on screen for a 
//while and hides automatically 
rendermessage=(type,displaymessage)=>{
    if(type=="success"){
        message.success(displaymessage)
    } else if(type=="error"){
        message.error(displaymessage)
    }
}

I have used Antd message to popup message on screen which automatically hides after a set time of interval. https://ant.design/components/message/

So what i am missing here is that i can't find out a proper way to render UI on frontend based on state change.

i would handle this scenario simply without redux, like there i would have the method to send request inside my component, and inside there in success or fail response i would render a message directly from there, rather than setting the state and then rendering message based on state.

like below

 uploadFile=(files) =>{

 this.setState({UploadResponse: null,UploadStatus:"InProcess"});
 axios.post(`${config.API_URL}/upload`, files)
 .then(res => {
    this.rendermessage("success","File uploaded successfully.");
    this.setState({UploadResponse: res.data,UploadStatus:"Completed"});
  
 })
 .catch(err =>{
    this.rendermessage("fail","Failed to uploaded.");
    this.setState({UploadResponse: res.data,UploadStatus:"Completed"});
 }
 )
 };

I have simply handled the scenario, because the method to send request is inside my component, and i just called this method when upload button is clicked. This reduced my render code as well like below

  render() {
  return (
  <div>
   <div className="upload-sect">
   {
      this.props.UploadStatus=="InProcess"?<Spin/>: <></>
   }
  </div>
  <div>
    {
      this.props.UploadStatus=="Completed"?
      this.renderUploadResponse(this.props.UploadResponse)
      :
      <></>
    }
</div>
</div>)}

what i am missing in redux? I think this is a very common scenario which occurs thousand times in react application. so it should be as simple to handle. what i am missing is that i can't find out the standard way to use redux?

Am I right to think that your logic to render the component varies when you decide using redux instead of managing state at component level?

Can you suggest what should exactly be the title of this question?

Updated

From the comment section below i have found that most of people are not understanding my question so i need to put up some more effort to be more specific.

The given problem in my question is just one case which i have presented.There can be different workaround to solve this problem , one of which i have adopted and fix the issue, that i will come to later, but first let me emphasize on my question.

I have shown while managing the state at component level, I just handled the UI with much simplicity. e.g. on success or fail of my asynch call , i just called a method which render the popup on screen , so this popup was not attached to any state change , rather its rendered directly in result of an action. whether you use ANTD message or any other notification provider that doesn't matter , the thing is that it has to be disappear automatically (under notification provider functionality notification hide after a certain time interval)

so in normal (i am calling normal to reactjs without redux) reactjs I can achieve this behavior simply . e.g. render the message without any state change and i will disappear automatically.

but in redux i can't make it that simple. the details i have already put up in my question.

Now what solution i have adopted to solve the issue is as below

I have come to conclude that to handle it with redux i have to make two dispatches to handle the scenario , i made a very small change in my code , lets put it below so that you guys not to scroll up to re visit the code

export const uploadFile = (files) => (dispatch)

<details>
<summary>英文:</summary>

I have read through lots of tutorial about using redux in reactjs but there are still few things which i am missing. Below is my action method 

    export const uploadFile = (files) =&gt; async (dispatch) =&gt; {
    
    dispatch({ 
        type: T.UPLOAD_REQUEST,
        payload:{UploadStatus:&quot;InProcess&quot;}
     });
    axios.post(`${config.API_URL}/upload`, files)
        .then(res =&gt; {
            dispatch({ 
                type: T.UPLOAD_SUCCESS, 
                payload: { UploadResponse: res.data,UploadStatus:&quot;Completed&quot;} 
            });
        }).then(res=&gt;{

            dispatch({ 
                type: T.UPLOAD_SUCCESS, 
                payload: { UploadResponse: null,UploadStatus:&quot;Queued&quot;} 
            });

        })
        .catch(err =&gt;{
            console.log(err);
            dispatch({ 
                type: T.UPLOAD_FAIL, 
                payload: { UploadResponse: err,UploadStatus:&quot;Error&quot; } 
               
            });
        }
        ).then(res=&gt;{

            dispatch({ 
                type: T.UPLOAD_FAIL, 
                payload: { UploadResponse: null,UploadStatus:&quot;Queued&quot;} 
            });

        })
    };

this action uploads file on server and dispatches to reducer to make updates on UI. &quot;UploadResponse&quot; and &quot;UploadStatus&quot; are two state fields which i need to render the UI on frontend. 

when request starts I set `{UploadStatus:&quot;InProcess&quot;}` to render a spinner. After the requests succeeds or fails i set this status fields to &quot;Completed&quot; or &quot;Error&quot; accordingly. this helps me to remove the spinner and render success or fail message. 

when request succeeds and UploadStatus is set to &quot;Completed&quot; and a success message pops up on front end and after that user interacts further on component to re render the component, lets say I enter some text in textbox, what happens is UploadStatus still holds the value &quot;Completed&quot; and the message pops up again.

To solve this issue, i make 2 dispatches on each success or fail request response. e.g. you can see in above code, on call success i first dispatched   `{UploadStatus:&quot;Completed&quot;}` , and then `{UploadStatus:&quot;Queued&quot;} `to reset the status. it helps me in a way that on first dispatch i render the success message on screen, then i reset the Upload Status so that the message to not popup on further interactions on component. 

But I have to render a div on frontend when UploadStatus is set to completed, and this div has to be there even after further actions on component. On my second dispatch the UploadStatus is set to &quot;Queued&quot; so the div has disappeared.

I will further add my component code below to explain my logic to render the UI 

    render() {
      return (
      &lt;div&gt;
       &lt;div className=&quot;upload-sect&quot;&gt;
       {
          this.props.UploadStatus==&quot;InProcess&quot;?&lt;Spin/&gt;: &lt;&gt;&lt;/&gt;
          this.props.UploadStatus==&quot;Completed&quot;? 
          this.rendermessage(&quot;success&quot;,&quot;uploded&quot;):&lt;&gt;&lt;/&gt;
          this.props.UploadStatus==&quot;Error&quot;?this.rendermessage(&quot;error&quot;,&quot;Upload 
          Fail&quot;):&lt;&gt;&lt;/&gt;
       }
      &lt;/div&gt;
      &lt;div&gt;
        {
          this.props.UploadStatus==&quot;Completed&quot;?
          this.renderUploadResponse(this.props.UploadResponse)
          :
          &lt;&gt;&lt;/&gt;
        }
    &lt;/div&gt;
    &lt;/div&gt;)}
    
Below is my render message fucntion

    //message is ANTD design control , which pops up message on screen for a 
    //while and hides automatically 
    rendermessage=(type,displaymessage)=&gt;{

        if(type==&quot;success&quot;){
            message.success(displaymessage)
        } else if(type==&quot;error&quot;){
            message.error(displaymessage)
        }
    }

I have used Antd message to popup message on screen which automatically hides after a set time of interval. https://ant.design/components/message/

So what i am missing here is that i can&#39;t find out a proper way to render UI on frontend based on state change. 

i would handle this scenario simply without redux, like there i would have the method to send request inside my component, and inside there in success or fail response i would render a message directly from there, rather than setting the state and then rendering message based on state. 

like below 

     uploadFile=(files) =&gt;{
     this.setState({UploadResponse: null,UploadStatus:&quot;InProcess&quot;});
     axios.post(`${config.API_URL}/upload`, files)
     .then(res =&gt; {
        this.rendermessage(&quot;success&quot;,&quot;File uploaded successfully.&quot;);
        this.setState({UploadResponse: res.data,UploadStatus:&quot;Completed&quot;});
      
     })
     .catch(err =&gt;{
        this.rendermessage(&quot;fail&quot;,&quot;Failed to uploaded.&quot;);
        this.setState({UploadResponse: res.data,UploadStatus:&quot;Completed&quot;});
     }
     )
     }; 

I have simply handled the scenario, because the method to send request is inside my component, and i just called this method when upload button is clicked. This reduced my render code as well like below 

      render() {
      return (
      &lt;div&gt;
       &lt;div className=&quot;upload-sect&quot;&gt;
       {
          this.props.UploadStatus==&quot;InProcess&quot;?&lt;Spin/&gt;: &lt;&gt;&lt;/&gt;
       }
      &lt;/div&gt;
      &lt;div&gt;
        {
          this.props.UploadStatus==&quot;Completed&quot;?
          this.renderUploadResponse(this.props.UploadResponse)
          :
          &lt;&gt;&lt;/&gt;
        }
    &lt;/div&gt;
    &lt;/div&gt;)}

what i am missing in redux? I think this is a very common scenario which occurs thousand times in react application. so it should be as simple to handle. what i am missing is that i can&#39;t find out the standard way to use redux?

Am I right to think that your logic to render the component varies when you decide using redux instead of managing state at component level?


Can you suggest what should exactly be the title of this question?

**Updated**


**From the comment section below i have found that most of people are not understanding my question so i need to put up some more effort to be more specific.**


The given problem in my question is just one case which i have presented.There can be different workaround to solve this problem , one of which i have adopted and fix the issue, that i will come to later, but first let me emphasize on my question.

I have shown while managing the state at component level, I just handled the UI with much simplicity. e.g. on success or fail of my asynch call , i just called a method which render the popup on screen , so this popup was not attached to any state change , rather its rendered directly in result of an action. whether you use ANTD message or any other notification provider that doesn&#39;t matter , the thing is that it has to be disappear automatically (under notification provider functionality notification hide after a certain time interval)

so in normal (i am calling normal to reactjs without redux) reactjs I can achieve this behavior simply . e.g. render the message without any state change and i will disappear automatically.

but in redux i can&#39;t make it that simple. the details i have already put up in my question.

Now what solution i have adopted to solve the issue is as below

I have come to conclude that to handle it with redux i have to make two dispatches to handle the scenario , i made a very small change in my code , lets put it below so that you guys not to scroll up to re visit the code

    export const uploadFile = (files) =&gt; (dispatch) =&gt; {

    dispatch({ type: T.UPLOAD_REQUEST,payload:{UploadStatus:&quot;InProcess&quot;}});
    axios.post(`${config.API_URL}/upload`, files}).then(res =&gt; {            
    dispatch({ type: T.UPLOAD_SUCCESS, payload: { UploadResponse:res.data,
    UploadStatus:&quot;Completed&quot;,MessageToBeDelievered:true} });
    }).then(res=&gt;{
    dispatch({ type: T.UPLOAD_SUCCESS, 
            payload: { MessageToBeDelievered:false} 
        });
    })
    .catch(err =&gt;{
        dispatch({ 
            type: T.UPLOAD_FAIL, payload: { UploadResponse:err,
             UploadStatus:&quot;Error&quot;,MessageToBeDelievered:true }  
        });
    }).then(res=&gt;{

        dispatch({ type: T.UPLOAD_FAIL, 
        payload: {MessageToBeDelievered:false} 
        });

    })
    };
So no big change , I just keep an extra bit &quot;MessageToBeDelievered&quot; in first dispatch after a success or fail call MessageToBeDelievered is set to true , so that to render the popup , and then in next dispatch it is set to false . pretty simple right ? .. I didn&#39;t change the UploadStatus in second dispatch , so its value remains &quot;Completed&quot; after a successful call , even after second dispatch. so the div which i need to show on successful call , appears and rests there , unless the UploadStatus goes to &quot;InProcess&quot; again.

No big deal. so again coming to the actual question , my question is

(i) Do i need to think differently about my UI render logic while using redux and while not using it.bcs i thought redux is just another way to handle the state and it shouldn&#39;t belong to the render logic of my component, but its making me think of the component render logic to be change a bit which i was not expecting from redux.

(ii) Apart from this popup message (which hides automatically) , so far i couldn&#39;t find any other scenario like this , but are there any other scenarios like this ? any example ?




   









</details>


# 答案1
**得分**: 1

**具体解决方案**

[ANTD `Message` 组件文档][1] 提到了一个 `onClose`函数参数你可以传递给这个控件

你传递的 `onClose` 函数将在你指定的持续时间后由 `Message` 组件调用

你应该传递一个函数该函数会更新你的 Redux 状态中的 `UploadStatus` 属性将其设置为除了 'Completed' 之外的某个值例如 'Inactive')。

**一般思考**

你不必为所有状态都使用 Redux将组件状态与 Redux 混合使用是完全有效的我倾向于使用组件状态来存储仅与单个组件及其后代相关的状态或者是短暂的状态我使用 Redux 来存储被多个组件使用和/或持续时间较长的状态

如果一个组件依赖于 Redux 状态你将不得不管理相关的 Redux 状态以控制组件在后续渲染时的行为正如我上面建议的那样)。正如你所注意到的这可能会使某些事情比依赖于组件状态的解决方案稍微复杂一些

我不知道你具体示例的完整上下文但如果你显示的状态不被其他组件需要我会倾向于将其存储在组件状态中如果其他组件需要该状态我会将其存储在 Redux 并按照我上面建议的方式进行管理

  [1]: https://ant.design/components/message/

<details>
<summary>英文:</summary>

**Specific Solution**

The [ANTD `Message` component documentation][1] mentions an `onClose` (function) argument that you can pass to the control.

The `onClose` function you pass will be called by the `Message` component when it closes after the duration you specified.

You should pass a function that updates the `UploadStatus` property in your redux state to some value other than &#39;Completed&#39; (e.g. &#39;Inactive&#39;).

**General Thoughts**

You don&#39;t have to use redux for all state. It&#39;s perfectly valid to mix component state with redux. I tend to use component state to store state that is only relevant for a single component and its descendants and/or is short-lived state. I use redux for state that is used by multiple components and/or longer-lived state.

If a component depends on redux state, you&#39;ll have to manage the relevant redux state in order to control the component&#39;s behavior on subsequent renders (e.g. as I&#39;ve suggested above). As you&#39;ve noted, this can make some things slightly more complicated than a solution depending on component state.

I don&#39;t know the full context of your specific example, but if the state you&#39;ve shown isn&#39;t needed by other components, I would lean toward storing it in component state. If the state is needed by other components, I would store it in redux and take care of managing it as I suggested above.

  [1]: https://ant.design/components/message/

</details>



# 答案2
**得分**: -1

使用 Redux

Redux将UploadStatus属性传递到您的组件中在每次渲染按键等等您都会使用UploadStatus及其相应的ANTD的rendermessage重新渲染这会导致rendermessage运行多次因为rendermessage的使用方式不正确

不使用 Redux

没有传递会被反复渲染的prop在函数完成后只调用一次ANTD的rendermessage

您有多种处理方法

1. 不要将ANTD的rendermessage与UploadStatus连接起来只需按照您已经设置的方式使用它不使用Redux
2. 如果必须使用Redux创建一个单独的组件来处理它并使用React的useEffect来触发rendermessage

<details>
<summary>英文:</summary>

With redux

Redux passes the UploadStatus property into your component. On every render (keystroke, etc) you re-render with UploadStatus and its respective ANTD&#39;s rendermessage. This causes rendermessage to run multiple times since rendermessage is used incorrectly.

Without redux

No prop is passed that gets rendered repeatedly. You only call ANTD&#39;s rendermessage once after the function is completed.

You have multiple options to deal with it. 

 1. Don&#39;t connect ANTD&#39;s rendermessage to UploadStatus. Just use it the way you already have it setup without redux.
 2. If you must use redux,create a separate component to handle it and React&#39;s useEffect to trigger rendermessage.

</details>



huangapple
  • 本文由 发表于 2020年1月6日 02:55:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/59603167.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定