how to setstate after saga async request
Asked Answered
B

4

11

I'm using redux-saga in my project. I used redux-thunk before, so i can't setState ends of some async request. like

this.props.thunkAsync()
  .then(){
    this.setState({ '' });
  }

Since thunk returns promise, i could use 'then'. But i can't do this with saga, because saga doesn't return promise. So i did it in componentWillReceiveProps by checking flag props (like REQUEST_SUCCESS,REQUEST_WAITING...) has been changed. I think it's not good way to solve this problem.

So.. My question is how can i do some works when async request ends in redux-saga!

Befool answered 22/11, 2017 at 4:48 Comment(5)
you can put the react's state in redux state so that you can set the state in saga itself and make use of the redux state in this component.Schott
i thought about that.. thanks! But what if i want to call some function that only works locally?Befool
If you are using redux to manage state, then you can fire an action and handle it in your reducer to set state.Abstracted
@Befool you should most probably move those functions to saga if you can.Schott
@AnandS thanks! But what if i want to change inline style after async request end? Should i move this style object(or some local variable) to my redux state?Befool
R
3

Every api call you make is processed as an async request but handled using a generator function in a saga.

So, After a successful api call, you can do the following possible things.

  1. Make another api call like
function* onLogin(action) {
  try {
    const { userName, password } = action;
    const response = yield call(LoginService.login, userName, password);
    yield put(LoginService.loginSuccess(response.user.id));
    const branchDetails = yield call(ProfileService.fetchBranchDetails, response.user.user_type_id);
    yield put(ProfileActions.fetchBranchDetailsSuccess(branchDetails));
  } catch (error) {
    yield put(ProfileActions.fetchUserDetailsError(error));
  }
}
  1. Pass a Callback after successfull api

    onLoginClick() {
      const { userName, password } = this.state;
      this.props.login(userName, password, this.onLoginSuccess);
    }
    
    onLoginSuccess(userDetails) {
      this.setState({ userDetails });
    }
    
    function *onLogin(action) {
      try {
         const { userName, password, onLoginSuccess } = action;
         const response = yield call(LoginService.login, userName, password);
         if (onLoginSuccess) {
             onLoginSuccess(response);
         }
    
         yield put(LoginService.loginSuccess(response.user.id));
         const branchDetails = yield call(ProfileService.fetchBranchDetails, 
         response.user.user_type_id);
         yield put(ProfileActions.fetchBranchDetailsSuccess(branchDetails));
     } catch (error) {
         yield put(ProfileActions.fetchUserDetailsError(error));
     }
    

    }

  2. Update Reducer State and get from props by mapStateToProps

    yield put(LoginService.loginSuccess(response.user.id));        
    @connect(
        state => ({
            usedDetails: state.user.get('usedDetails'),
        })
    )
    static getDerivedStateFromProps(nextProps, prevState) {
        const { usedDetails } = nextProps;
        return {
            usedDetails
        }
    }
    
Ringtailed answered 19/7, 2018 at 11:10 Comment(0)
P
5

But i can't do this with saga, because saga doesn't return promise

Redux-saga is slightly different from thunk since it is process manager, not simple middleware: thunk performs reaction only on fired actions, but saga has its own "process" (Formally callback tick domain) and can manipulate with actions by effects.

Usual way to perform async actions with redux-saga is splitting original actions to ACTION_REQUEST, ACTION_SUCCESS and ACTION_FAILURE variants. Then reducer accepts only SUCCESS/FAILURE actions, and maybe REQUEST for optimistic updates.

In that case, your saga process can be like following

function* actionNameSaga(action) {
    try {    
        const info = yield call(fetch, { params: action.params }
        yield put('ACTION_NAME_SUCCESS', info)
    } catch(err) {
        yield put('ACTION_NAME_FAILURE', err)
    }

function* rootSaga() {
    yield takeEvery('ACTION_NAME', actionNameSaga)
}

Keep in mind that yield operation itself is not about promise waiting - it just delegates async waiting to saga process manager.

Picardi answered 22/11, 2017 at 7:38 Comment(0)
R
3

Every api call you make is processed as an async request but handled using a generator function in a saga.

So, After a successful api call, you can do the following possible things.

  1. Make another api call like
function* onLogin(action) {
  try {
    const { userName, password } = action;
    const response = yield call(LoginService.login, userName, password);
    yield put(LoginService.loginSuccess(response.user.id));
    const branchDetails = yield call(ProfileService.fetchBranchDetails, response.user.user_type_id);
    yield put(ProfileActions.fetchBranchDetailsSuccess(branchDetails));
  } catch (error) {
    yield put(ProfileActions.fetchUserDetailsError(error));
  }
}
  1. Pass a Callback after successfull api

    onLoginClick() {
      const { userName, password } = this.state;
      this.props.login(userName, password, this.onLoginSuccess);
    }
    
    onLoginSuccess(userDetails) {
      this.setState({ userDetails });
    }
    
    function *onLogin(action) {
      try {
         const { userName, password, onLoginSuccess } = action;
         const response = yield call(LoginService.login, userName, password);
         if (onLoginSuccess) {
             onLoginSuccess(response);
         }
    
         yield put(LoginService.loginSuccess(response.user.id));
         const branchDetails = yield call(ProfileService.fetchBranchDetails, 
         response.user.user_type_id);
         yield put(ProfileActions.fetchBranchDetailsSuccess(branchDetails));
     } catch (error) {
         yield put(ProfileActions.fetchUserDetailsError(error));
     }
    

    }

  2. Update Reducer State and get from props by mapStateToProps

    yield put(LoginService.loginSuccess(response.user.id));        
    @connect(
        state => ({
            usedDetails: state.user.get('usedDetails'),
        })
    )
    static getDerivedStateFromProps(nextProps, prevState) {
        const { usedDetails } = nextProps;
        return {
            usedDetails
        }
    }
    
Ringtailed answered 19/7, 2018 at 11:10 Comment(0)
M
1

I was stuck with the same problem...

My solution was wrapping the dispatch in a promise and call the resolve and reject in a saga function...

I created a hook to wrap the dispatch. You can see my example here: https://github.com/ricardocanelas/redux-saga-promise-example

I hope that can help somebody.

Maggie answered 25/1, 2020 at 18:12 Comment(0)
A
0

You can do it this way. From component props you call the saga method and pass the function you want to execute after success or failure, like below

export function* login({ payload }) {
  const url = 'localhost://login.json';

 try {
    const response = yield call(App_Service, { url, method: 'GET' });
 if (response.result === 'ok' && response.data.body) {
    yield put(fetchDataActionCreators.getLoginSuccess(response.data.body));
    //function passed as param from component, pass true if success
    payload.callback(true);
  }


 } catch (e) {
        //function passed as param from component, pass false if failure
    payload.callback(false);
    console.log(e);
  }
}


export function* watchLogin() {
  while (true) {
    const action = yield take(LOGIN);
    yield* login(action);
  }
}



export default function* () {
  yield all([
    fork(watchLogin)
  ]);
}

In component call call setState method in the function you pass to saga as param

login() {
    // store
    this.props.getServiceDetails({
       callback:(success) => this.onLoginSuccess(success)
    })
  }



onLoginSuccess = (success) => {
    this.setState({
       login:success
    })
    alert('login '+success)
  }
Acreinch answered 13/6, 2019 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.