Redux support

Redux support

This page describes the support provided for Redux. Since redux-saga is significantly different from other redux package, a specific section is dedicated to it. Other support is given in this section.

Redux is a predictable state container for JavaScript apps.In redux, state changes are triggered by dispatching given actions.When an action is dispatched, its reducer function handler is executed and updates the state.

The com.castsoftware.typescript extension creates a ReduxJS handler when an action reducer is found within TypeScript source code. It is named according to the action name. When dispatching to that action is found, a callLink is created to that ReduxJS handler.

Reducer handler creation

There exist several ways of defining a reducer.The following ways are supported (i.e. the com.castsoftware.typescript extension will create a ReduxJS handler):

API Package Limitation
TypeScript function taking a state as the first argument and an action as the second argument and containing switches based on the action.type NA no function should be used in the condition
createReducer @reduxjs/toolkit
createSlice @reduxjs/toolkit
handleActions redux-actions
createReducer redux-starter-kit

Examples

Example with a simple function

When analyzing the following source code:

const initialState = { value: 0 }
function counterReducer(state = initialState, action) {

  if (action.type === 'counter/increment') {
    // If so, make a copy of `state`
    return {
      ...state,
      // and update the copy with the new value
      value: state.value + 1
    }
  }
  elif (action.type === 'counter/decrement '){
    return {
      ...state,
      value: decrement(state)
    }
  }
  elif my_condition(action){
     //...
  }
}

two ReduxJS handler (counter/increment and *counter/decrement *) are created. A callLink is created from the *counter/decrement ReduxJS handler *and the *decrement *function (if that function is found). Not that if the condition is too complex (for instance if a function is used for evaluating the condition), no ReduxJS handleris created.

Example with createReducer from redux-toolkit

When analyzing the following source code:

import { createAction, createReducer } from '@reduxjs/toolkit'

const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')

export default const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state, action) => {
      state.value++
    })
    .addCase(decrement, (state, action) => {
      state.value--
    })
})

two ReduxJS handler (counter/increment and counter/decrement) are created with a callLink to the corresponding anonymous function given as second argument in the addCase calls.

Action dispatching

Several APIs allow dispatching an action. The following APIs are supported:

API Package Remark
configureStore().dispatch @reduxjs/toolkit
useDispatch react-redux
through props using map dispatch to props from connect react-redux see the Example dispatch to props
@dispatch decorator @angular-redux/store

Examples

Example with configureStore().dispatch

When analyzing the following source code:

import { configureStore } from '@reduxjs/toolkit'

function my_increment(){
    const store = configureStore({ reducer: counterReducer })
    store.dispatch({ type: 'counter/increment' })
}

assuming that a ReduxJS handler *counter/increment *exists, a callLink is created from my_increment TypeScript Function to the ReduxJS handler:

Example with map dispatch to props

When analyzing the following source codes:

import { connect } from 'react-redux';
import { Draggable } from './draggable';

const mapDispatchToProps = (dispatch) => {
  return {
    increment: () => dispatch({ type: 'counter/increment' }),
    decrementasprops: () => dispatch({ type: 'counter/decrement' }),
  }
}  

const ConnectedDraggable= connect(
  makeMapStateToProps,
  mapDispatchToProps,
  // ...
)(Draggable);

draggable.ts

export default class Draggable extends Component<Props> {

   constructor(props){
      super(props)
   }

   my_decrement(){
      this.props.decrementasprops()
   }

  my_increment(){
      this.props.increment()
  }
}

the com.castsoftware.typescript extension creates

  • a callLink is created between my_decrement method and the ReduxJS handler counter/decrement(if it exists)
  • a callLink is created between *my_increment * method and the ReduxJS handler *counter/increment *(if it exists)
Example with @dispatch decorator from  @angular-redux/store

When analyzing the following source code:

import { dispatch } from '@angular-redux/store';

class A {

   @dispatch()
   my_increment(){
      return {type: 'counter/increment'}
   }
}

assuming that a ReduxJS handler *counter/increment *exists, a callLink is created from my_increment TypeScript Method to that ReduxJS handler:

Redux-saga

Redux-saga is a redux side effect manager that is designed to handle asynchronicity. 

Supported APIs from redux-saga/effects:

APIs What result to expect (see examples section for more details) Known limitations
takeEvery, takeLatest, takeLeading, throttle, debounce create a ReduxJS handler (named based on the pattern provided in the first or second argument of the API call) with a callLink to the function given as second or third (depending on the API) argument of the API call does not work when the pattern is a function taking an action as argument
take, takeMaybe create a ReduxJS handler (named based on the first argument of the API call) with a callLink to the caller of the API
put, putResolve create a link to a given ReduxJS handler
call, cps, fork, spawn create a link to the function given as first argument of the API call

Examples

Example for takeEvery, takeLatest, takeLeading, throttle, debounce

When analyzing the following source code:

import { call, put, takeEvery} from 'redux-saga/effects'
 
// worker Saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
  // do whatever
}

function my_pattern_for_delete_user(action){
  if (action.type == 'USER_DELETE'){
     return True
  }
}

function* deleteUser(action) {
  // do whatever
}

export function* mySaga() {
  yield takeEvery('USER_FETCH_REQUESTED', fetchUser)
  yield takeEvery(my_pattern_for_delete_user, deleteUser) 
}

a *USER_FETCH_REQUESTED ReduxJS handler *is created with a callLink to the fetchUser function.
Note that the use of function (such as my_pattern_for_delete_user in the previous example) is not supported. So no reducer is created in that case.

The support for takeLatest, takeLeading, throttle, debounce works as for take Every except that for throttle and debounce the action name is provided as the second argument (instead of first) and the callee is given as the third (instead of second) argument.

Example for take or takeMaybe

When analyzing the following source code:

import { take } from 'redux-saga/effects'

function* take_handler(){
   yield take('USER_FETCH_REQUESTED')
   // do whatever
}

a *USER_FETCH_REQUESTED ReduxJS handler *is created with a callLink to the take_handler function.

The support for takeMaybe works as for take.

Example for put or putResolve

When analyzing the following source code:

import { put } from 'redux-saga/effects'

function my_put(){
  yield put({ type: 'USER_FETCH_REQUESTED' })
}

assuming that a ReduxJS handler *USER_FETCH_REQUESTED *exists, a callLink is created from my_put TypeScript Method to that reducer:

The support for putResolve works as for take.

Example for call, cps, fork or spawn

When analyzing the following source code:

import { call } from 'redux-saga/effects'

function* my_caller(action) {
  call(my_callee)
}

function* my_callee(){
}

a callLink is added between my_caller and my_callee functions:

The support for cps, fork and spawn works as for call.