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.