async redux. Error: modifiers may not emit actions - javascript

I keep encountering what appears to be a parallel async redux error. At a high level I am trying to dispatch a bunch of async actions that fetch assets from urls.
I originally tried redux-thunk middleware and was encountering the same error. I have since moved to a "listener" style middleware which looks like:
// listener-middleware.js
// taken from https://medium.com/#alexandereardon/the-middleware-listener-pattern-better-asynchronous-actions-in-redux-16164fb6186f
export default (...listeners) => store => next => action => {
// listeners are provided with a picture
// of the world before the action is applied
const preActionState = store.getState();
// release the action to reducers before
// firing additional actions
next(action);
// always async
setTimeout(() => {
// can have multiple listeners listening
// against the same action.type
listeners.forEach(listener => {
if (listener[action.type]) {
listener[action.type](action, store.dispatch, preActionState);
}
});
});
};
The asset listener then looks like:
import { assetsFetchBinary, assetsReceiveBinary } from '../assets/actions.js';
export default {
[assetsFetchBinary.toString()]: (action, dispatch, state) => {
const assetPath = state.config.assetPath + '/' + action.payload.assetName;
if(!state.assets[action.payload.assetName]){
fetch(assetPath)
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
return dispatch(assetsReceiveBinary(action.payload.assetName, arrayBuffer))
});
}
},
}
The store configuration and applyMiddleware is:
import { createStore, applyMiddleware } from 'minidux'
import rootReducer from './rootReducer';
import { createLogger } from 'redux-logger';
import { routerMiddleware } from 'react-router-redux'
import { sceneTickAction } from './dsl/scene/actions.js';
import assetMiddleware from './dsl/assets/middleware.js';
import listenerMiddleware from './listener-middleware';
const logger = (initialState) => {
const predicate = (getState, action) => {
if(action.type === sceneTickAction.toString()){
return false;
}
return true;
}
return createLogger({predicate});
};
const getMiddleWare = (initialState, history) => {
const list = [];
if(initialState.system.role === 'client'){
const routerHistory = routerMiddleware(history);
list.push(routerHistory);
list.push(listenerMiddleware(assetMiddleware))
list.push(logger(initialState));
return list;
};
const configureStore = (initialState, history) => {
return createStore(
rootReducer(initialState),
initialState,
applyMiddleware.apply(this, getMiddleWare(initialState, history))
);
}
export default configureStore
The actions are straight forward
import { createAction } from 'redux-actions';
export const assetsReceiveBinary = createAction('#ASSETS/RECEIVE_BINARY')
export const assetsFetchBinary = createAction('#ASSETS/FETCH_BINARY')
and the Reducers:
import { handleActions } from 'redux-actions';
import { assetsFetchBinary, assetsReceiveBinary } from '../assets/actions.js';
export const assetReducer = (state, action) => handleActions({
[assetsFetchBinary]: (state, action) => {
if(state.assets[action.path]){ return state; }
return {
...state,
assets: {
...state.assets,
[action.path]: {}
}
}
},
[assetsReceiveBinary]: (state, action) => {
return {
...state,
assets: {
...state.assets,
[action.path]: {arrayBuffer: action.arrayBuffer}
}
}
}
}, state)(state, action);
export default assetReducer;
The stack trace is as follows:
createStore.js:27 Uncaught Error: modifiers may not emit actions
at dispatch (createStore.js:27)
at listener-middleware.js:10
at middleware.js:13
at Object.assetsFetchBinary (bindActionCreators.js:3)
at CharacterView.jsx:21
at Array.forEach (<anonymous>)
at CharacterView.componentDidUpdate (CharacterView.jsx:20)
at commitLifeCycles (react-dom.development.js:11517)
at commitAllLifeCycles (react-dom.development.js:12294)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at commitAllWork (react-dom.development.js:12415)
at workLoop (react-dom.development.js:12687)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at performWork (react-dom.development.js:12800)
at scheduleUpdateImpl (react-dom.development.js:13185)
at scheduleUpdate (react-dom.development.js:13124)
at Object.enqueueSetState (react-dom.development.js:9646)
at Connect../node_modules/react/cjs/react.development.js.ReactComponent.setState (react.development.js:
dispatch line 27 looks like:
function dispatch (action) {
if (!action || !isPlainObject(action)) throw new Error('action parameter is required and must be a plain object')
if (!action.type || typeof action.type !== 'string') throw new Error('type property of action is required and must be a string')
if (isEmitting) throw new Error('modifiers may not emit actions')
The action at that dispatch call looks like {type: "#ASSETS/FETCH_BINARY"}
Additionally, I invoke these actions in componentDidMount like the following:
class CharacterView extends Component {
componentDidUpdate(){
if(this.props.missingItemAssets){
this.props.missingItemAssets.forEach((assetName) => {
this.props.assetsFetchBinary(assetName);
});
}
}
If I wrap the dispatch calls in a 0 setTimeout it prevents the error.
class CharacterView extends Component {
componentDidUpdate(){
if(this.props.missingItemAssets){
setTimeout(() => {
this.props.missingItemAssets.forEach((assetName) => {
this.props.assetsFetchBinary(assetName);
});
}, 0)
}
}

You mentioned that you dispatch the actions in componentDidMount, but your example shows you're actually dispatching from componentDidUpdate.
class CharacterView extends Component {
componentDidUpdate(){ // <--- should be componentDidMount?
if(this.props.missingItemAssets){
this.props.missingItemAssets.forEach((assetName) => {
this.props.assetsFetchBinary(assetName);
});
}
}
}
This might be causing your problem, assuming your example in the question is accurate.

Related

Generic modals with Redux and Thunk

I've been looking into creating generic modals with React, Redux, and Thunk. Ideally, my state would look like the following:
export interface ConfirmModalState {
isOpened: boolean;
onConfirm: null | Function
}
export const initialConfirmModalState: ConfirmModalState = {
isOpened: false,
onConfirm: null
};
However, this would mean putting non-serializable data into the state, which seems to be highly discouraged.
I've read a great blogpost by markerikson. However, I don't think the proposed solution would work with asynchronous actions and Thunk.
How do you suggest to resolve this issue?
I actually wrote the post that you linked, and I wrote a much-expanded version of that post a couple years later:
Practical Redux, Part 10: Managing Modals and Context Menus.
I've actually implemented a couple variations of this approach myself since I wrote that post, and the best solution I've found is to add a custom middleware that returns a promise when you dispatch a "show modal" action, and resolves the promise with a "return value" when the dialog is closed.
There's an existing implementation of this approach at https://github.com/AKolodeev/redux-promising-modals . I ended up making my own implementation. I have a partial version of my homegrown approach in a gist at https://gist.github.com/markerikson/8cd881db21a7d2a2011de9e317007580 , and the middleware looked roughly like:
export const dialogPromiseMiddleware: Middleware<DialogPromiseDispatch> = storeAPI => {
const dialogPromiseResolvers: Record<string, Resolver> = {};
return next => (action: AnyAction) => {
switch (action.type) {
// Had to resort to `toString()` here due to https://github.com/reduxjs/redux-starter-kit/issues/157
case showDialogInternal.toString(): {
next(action);
let promiseResolve: Resolver;
const dialogPromise = new Promise((resolve: Resolver) => {
promiseResolve = resolve;
});
dialogPromiseResolvers[action.payload.id] = promiseResolve!;
return dialogPromise;
}
case closeDialog.toString(): {
next(action);
const {id, values} = action.payload;
const resolver = dialogPromiseResolvers[id];
if (resolver) {
resolver(values);
}
delete dialogPromiseResolvers[id];
break;
}
default:
return next(action);
}
};
};
(note: I made that gist when I was having some TS syntax issues getting dispatching to work correctly, so it's likely it won't 100% work out of the box. RTK also now includes some .match() action matching utilities that would be useful here. but, it shows the basic approach.)
The rough usage in a component is:
const closedPromise = dispatch(showDialog("TestDialog", {dialogNumber : counter});
const result = await closedPromise
// do something with the result
That way you can write the "on confirm" logic write there in the place that asked for the dialog to be shown in the first place.
Thank you markerikson for providing an answer. This inspired me to create a solution with thunks. Please give me some feedback here :)
I will be using hooks and #reduxjs/toolkit in my example.
This is the state of my ConfirmationModal reducer:
export interface confirmationModalState {
isOpened: boolean;
isConfirmed: boolean;
isCancelled: boolean;
}
export const initialConfirmationModalState: confirmationModalState = {
isOpened: false,
isConfirmed: false,
isCancelled: false,
};
This is the slice (a combination of the reducer and actions):
import { createSlice } from '#reduxjs/toolkit';
import { initialConfirmationModalState } from './state';
const confirmationModalSlice = createSlice({
name: 'controls/confirmationModal',
initialState: initialConfirmationModalState,
reducers: {
open: state => {
state.isOpened = true;
state.isConfirmed = false;
state.isCancelled = false;
},
confirm: state => {
state.isConfirmed = true;
state.isOpened = false;
},
cancel: state => {
state.isCancelled = true;
state.isOpened = false;
},
},
});
export const confirmationModalActions = confirmationModalSlice.actions;
export default confirmationModalSlice;
This is the thunk action for it:
import { createAsyncThunk } from '#reduxjs/toolkit';
import ThunkApiConfig from '../../../types/ThunkApiConfig';
import { AppState } from '../../reducers';
import { confirmationModalActions } from './slice';
const confirmationModalThunkActions = {
open: createAsyncThunk<boolean, void, ThunkApiConfig>(
'controls/confirmationModal',
async (_, { extra, dispatch }) => {
const store = extra.store;
dispatch(confirmationModalActions.open());
return await new Promise<boolean>(resolve => {
store.subscribe(() => {
const state: AppState = store.getState();
if (state.controls.confirmationModal.isConfirmed) {
resolve(true);
}
if (state.controls.confirmationModal.isCancelled) {
resolve(false);
}
});
});
},
),
};
export default confirmationModalThunkActions;
You can notice it uses extra.store to perform the subscribe. We need to provide it when creating a store:
import combinedReducers from './reducers';
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit';
import { ThunkExtraArguments } from '../types/ThunkExtraArguments';
function createStore() {
const thunkExtraArguments = {} as ThunkExtraArguments;
const customizedMiddleware = getDefaultMiddleware({
thunk: {
extraArgument: thunkExtraArguments,
},
});
const store = configureStore({
reducer: combinedReducers,
middleware: customizedMiddleware,
});
thunkExtraArguments.store = store;
return store;
}
export default createStore();
Now, let's create a hook that allows us to dispatch all of the above actions:
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from '../../../reducers';
import { useCallback } from 'react';
import confirmationModalThunkActions from '../thunk';
import { confirmationModalActions } from '../slice';
import { AppDispatch } from '../../../../index';
export function useConfirmationModalState() {
const dispatch: AppDispatch = useDispatch();
const { isOpened } = useSelector((state: AppState) => ({
isOpened: state.controls.confirmationModal.isOpened,
}));
const open = useCallback(() => {
return dispatch(confirmationModalThunkActions.open());
}, [dispatch]);
const confirm = useCallback(() => {
dispatch(confirmationModalActions.confirm());
}, [dispatch]);
const cancel = useCallback(() => {
dispatch(confirmationModalActions.cancel());
}, [dispatch]);
return {
open,
confirm,
cancel,
isOpened,
};
}
(don't forget to attach confirm and cancel to the buttons in your modal)
And that's it! We can now dispatch our confirmation modal:
export function usePostControls() {
const { deleteCurrentPost } = usePostsManagement();
const { open } = useConfirmationModalState();
const handleDelete = async () => {
const { payload: isConfirmed } = await open();
if (isConfirmed) {
deleteCurrentPost();
}
};
return {
handleDelete,
};
}

Function not receiving updated redux state even on re-render

I'm currently using Redux, Redux Thunk with NextJS and been trying to figure out how to access the updated redux state inside a function of a functional component.
As you can see in my code below, in the handleSubmit function, I want to update the redux state and then check the state value and decided which route it should take the user to.
Previously in my old project, using mapStateToProps with a Class component, I was able to access the updated redux state inside my handleSubmit function however when using a functional component both options (useSelector hook or mapStateToProps with connect()) doesn't seem to work.
At first I thought the component wasn't re-rendering however when checking the state in useEffect(), I can see that the state is getting updated and the component is able to view the updated values.
Is there something I'm clearly missing or is this way not possible with functional components?
loginPage.tsx
import LoginForm, { FormData } from 'components/Forms/LoginForm';
import Layout from 'components/Layout';
import { FORM_ERROR } from 'final-form';
import StatusCodes from 'lib/enums/statusCodes';
import { storeAuthToken } from 'lib/helpers/auth';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ApplicationState } from 'redux/store';
const LoginPage = () => {
const router = useRouter();
const dispatch = useDispatch();
const { auth } = useSelector((state: ApplicationState) => ({ auth: state.auth }));
const handleSubmit = async (values: FormData) => {
if (values && values.username && values.password) {
try {
// Updates redux store
await storeAuthToken(dispatch, values.username, values.password);
} catch (error) {
if (error === StatusCodes.BadRequest) {
return { [FORM_ERROR]: 'Sorry, you have entered incorrect details. Please try again' };
} else {
return { [FORM_ERROR]: 'Sorry, there was an issue trying to log you in' };
}
}
// Can't see updated values
console.log('Auth: ', auth);
if (auth.parsedJwt && auth.parsedJwt.changePassword) {
router.push({ pathname: '/update-password' });
return;
}
router.push('/dashboard');
}
};
useEffect(() => {
// Can see the updated values
console.log('New Auth: ', auth);
}, [auth]);
return (
<Layout hideProfileMenu title="Login">
<LoginForm onSubmit={handleSubmit} />
</Layout>
);
};
export default LoginPage;
I've attached the store and reducer incase I've set it up wrong.
store.tsx
import Auth from 'lib/interfaces/auth';
import { Context, createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper';
import { AnyAction, applyMiddleware, CombinedState, combineReducers, createStore, Dispatch, Reducer } from 'redux';
import thunkMiddleware, { ThunkAction, ThunkDispatch, ThunkMiddleware } from 'redux-thunk';
import authReducer from './auth/reducer';
export interface ApplicationState {
auth: Auth
}
const isDebug = process.env.NODE_ENV !== 'production';
const bindMiddleware = (middleware: ThunkMiddleware) => {
if (isDebug) {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(middleware));
}
return applyMiddleware(middleware);
};
const combinedReducer: Reducer<ApplicationState> = combineReducers<ApplicationState>({
auth: authReducer
});
const reducer = (state: ApplicationState, action: AnyAction) => {
if (action.type === HYDRATE) {
const nextState: CombinedState<ApplicationState> = {
...state,
...action.payload
};
return nextState;
} else {
return combinedReducer(state, action);
}
};
const makeStore: MakeStore<ApplicationState> = (_context: Context) => createStore(reducer as Reducer<ApplicationState, AnyAction>, bindMiddleware(thunkMiddleware));
export const wrapper = createWrapper<ApplicationState>(makeStore, { debug: isDebug });
reducer.tsx
import Auth from 'lib/interfaces/auth';
import { Reducer } from 'redux';
import { ActionTypes, AuthAction } from './actions';
const reducer: Reducer<Auth, AuthAction> = (state: Auth = {} as Auth, action: AuthAction): Auth => {
switch (action.type) {
case ActionTypes.UpdateToken:
return Object.assign({}, state, { token: action.token });
case ActionTypes.UpdateRefreshToken:
return Object.assign({}, state, { refreshToken: action.refreshToken });
case ActionTypes.UpdateParsedJwt:
return Object.assign({}, state, { parsedJwt: action.parsedJwt });
case ActionTypes.UpdateUuid:
return Object.assign({}, state, { uuid: action.uuid });
default:
return state;
}
};
export default reducer;
I think the simplest solution is to return the auth object from 'storeAuthToken' as it keeps the error handling and result in the same logical flow, and it fixes the asynchronous issue identified in the comments.
import LoginForm, { FormData } from 'components/Forms/LoginForm';
import Layout from 'components/Layout';
import { FORM_ERROR } from 'final-form';
import StatusCodes from 'lib/enums/statusCodes';
import { storeAuthToken } from 'lib/helpers/auth';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { ApplicationState } from 'redux/store';
const LoginPage = () => {
const router = useRouter();
const dispatch = useDispatch();
const handleSubmit = async (values: FormData) => {
if (values && values.username && values.password) {
try {
// Updates redux store
const authResult = await storeAuthToken(dispatch, values.username, values.password);
if (authResult.parsedJwt && authResult.parsedJwt.changePassword) {
router.push({ pathname: '/update-password' });
return;
}
} catch (error) {
if (error === StatusCodes.BadRequest) {
return { [FORM_ERROR]: 'Sorry, you have entered incorrect details. Please try again' };
} else {
return { [FORM_ERROR]: 'Sorry, there was an issue trying to log you in' };
}
}
router.push('/dashboard');
}
};
return (
<Layout hideProfileMenu title="Login">
<LoginForm onSubmit={handleSubmit} />
</Layout>
);
};
export default LoginPage;

What is the best option to use redux actions when using redux hooks?

I want to use redux hook useSelector to access the store and get rid of connect(), so I need to create a way to export my actions, and I'm thinking on a class with static methods, here is an example
export default class AuthActions {
static async login(userData) {
try {
const user = await axios.post('http://localhost:5000', userData);
dispatch({
type: AUTH.LOGIN,
payload: user.data
})
} catch (error) {
dispatch({
type: SET_ERROR,
payload: error
})
}
}
static setUser() {
console.log("SET USER")
}
static logout() {
console.log("Logout")
}
}
And then I use the action methods as follows:
import React from 'react';
import AuthActions from '../../redux/actions/AuthActions';
import { useSelector } from 'react-redux';
export default const Login = () => {
//More logic....
const { isAuth } = useSelector((state) => state.auth);
const submitHandler = e => {
e.preventDefault();
AuthActions.login(userData)
}
return (
<form onSubmit={submitHandler}>
My Login form ....
</form>
);
};
But I'm wondering if there is a disadvantage or performance issues to use redux in this way, or should I avoid the usage of a class and use a simple object instead?
Thank you in advance
this is my format of a reducer, inspired by ducks-modular-redux
for example, check out this darkMode reducer:
export const constants = {
TOGGLE: "darkMode/TOGGLE"
};
export const actions = {
toggleDarkMode: () => {
return {
type: constants.TOGGLE
};
}
};
export const thunks = {
toggleDarkMode: () => {
return (dispatch, getState) => {
dispatch(actions.toggleDarkMode());
const isDark = getState().darkMode.isDark;
localStorage.setItem("isDark", isDark);
};
}
};
const initialState = { isDark: localStorage.getItem("isDark") === "true" };
export default (state = initialState, action) => {
switch (action.type) {
case constants.TOGGLE:
return {
isDark: !state.isDark
};
default:
return state;
}
};

React Redux - Actions must be plain objects. Use custom middleware for async actions

I try to deal with ajax data using axom in my learning react,redux project and I have no idea how to dispatch an action and set the state inside a component
In component will mount
componentWillMount(){
this.props.actions.addPerson();
}
Store
import { createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers";
import thunk from "redux-thunk";
export default function configureStore() {
return createStore(rootReducer, applyMiddleware(thunk));
}
In Action :
import * as types from "./action-types";
import axios from "axios";
export const addPerson = person => {
var response = [];
axios
.get(`&&&&&&&&&&&`)
.then(res => {
response = res.data;
return {
type: types.ADD_PERSON,
response
};
});
};
In reducer
import * as types from "../actions/action-types";
export default (state = [], action) => {
console.log("action======>", action);
switch (action.type) {
case types.ADD_PERSON:
console.log("here in action", action);
return [...state, action.person];
default:
return state;
}
};
I am getting Actions must be plain objects. Use custom middleware for async actions.
You should use dispatch for async function. Take a look of the redux-thunk's documentation: https://github.com/gaearon/redux-thunk
In Action:
import * as types from "./action-types";
import axios from "axios";
export const startAddPerson = person => {
return (dispatch) => {
return axios
.get(`https://599be4213a19ba0011949c7b.mockapi.io/cart/Cart`)
.then(res => {
dispatch(addPersons(res.data));
});
}
};
export const addPersons = personList => {
return {
type: types.ADD_PERSON,
personList
};
}
In PersonComponent:
class Person extends Component {
constructor(props){
super(props);
}
componentWillMount() {
this.props.dispatch(startAddPerson())
}
render() {
return (
<div>
<h1>Person List</h1>
</div>
);
}
}
export default Redux.connect()(Person);
You need two actions here: postPerson and addPerson.
postPerson will perform the API request and addPerson will update the store:
const addPerson = person => {
return {
type: types.ADD_PERSON,
person,
}
}
const postPerson = () => {
return (dispatch, getState) => {
return axios.get(`http://599be4213a19ba0011949c7b.mockapi.io/cart/Cart`)
.then(res => dispatch(addPerson(res.data)))
}
}
in your component, call postPerson()
You use the redux-thunk library which gives you access to the "getState" and "dispatch" methods. I see that that has been added by Chenxi to your question. Run your async operation first within your action and then call "dispatch" with your simple action action creator, which will return the simple object that redux is looking for.
Here is what your async action creator and your simple action creator(broken out into two action creators) will look like:
export const addPersonAsync = (person) => {
return (dispatch) => {
var response = [];
axios
.get(`http://599be4213a19ba0011949c7b.mockapi.io/cart/Cart`)
.then(res => {
response = res.data;
dispatch(addPerson(response));
});
};
};
export const addPerson = (response) => ({
type: types.ADD_PERSON,
response
});
From your component, you'll now call the "addPersonAsync" action creator.

Empty state in component ( react redux )

Have problem with state in my component.
I'm trying to get status from my reducer but state is empty just getting undefined
Here is my actionCreator
export function checkLogin() {
return function(dispatch){
return sessionApi.authCheck().then(response => {
dispatch(authSuccess(true));
}).catch(error => {
throw(error)
})
}
}
My reducer
export const authStatus = (state = {}, action) => {
switch(action.type){
case AUTH_FALSE:
return{
status: action.status
}
case AUTH_TRUE:
return {
...state,
status: action.status
};
default:
return state;
}
};
And here is my component where i'm trying to get state
const mapStateToProps = (state) => {
return {
status: state.status
}
};
const mapDispatchToProps = (dispatch:any) => {
const changeLanguage = (lang:string) => dispatch(setLocale(lang));
const checkAuth = () => dispatch(checkLogin());
return { changeLanguage, checkAuth }
};
#connect(mapStateToProps, mapDispatchToProps)
I need to get status from the state
Component
import * as React from "react";
import Navigation from './components/navigation';
import { connect } from 'react-redux';
import { setLocale } from 'react-redux-i18n';
import cookie from 'react-cookie';
import {checkLogin} from "./redux/actions/sessionActions";
class App extends React.Component<any, any> {
constructor(props:any) {
super(props);
this.state = {
path: this.props.location.pathname
};
}
componentDidMount(){
this.props.checkAuth();
this.props.changeLanguage(cookie.load('lang'));
}
componentWillUpdate(){
}
render() {
return (
<div>
<Navigation path={this.state.path} />
{this.props.children}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
status: state.status
}
};
const mapDispatchToProps = (dispatch:any) => {
const changeLanguage = (lang:string) => dispatch(setLocale(lang));
const checkAuth = () => dispatch(checkLogin());
return { changeLanguage, checkAuth }
};
#connect(mapStateToProps, mapDispatchToProps)
export class Myapp
extends App {}
You cannot access props that are asynchronous inside of the constructor. As the constructor will be executed only once, when you instantiate your component. When you instantiate your component your asynchronous call has not responded yet, therefore this.props.status is undefined.
You could use componentWillReceiveProps from React lifecycle methods for example:
componentWillReceiveProps(nextProps) {
console.log(nextProps.status);
}
This method will be executed everytime a prop connected, or passed, to the component will change.
You could also use this.props.status inside of the render as this one is also executed everytime a prop changed.
For a better understanding of react lifecycle you could have the look at the different methods available, here : https://facebook.github.io/react/docs/react-component.html

Categories

Resources