Get latest Redux state in React immediately after state change - javascript

I have a simple redux action and reducer that changes state without making any sort of web service request:
// Action
export const setMyState = (value) => {
return {
type: 'SET_STATE',
payload: value
};
};
// Reducer
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_STATE':
return { myState: action.payload }
}
}
When I set the state via the action in my react component and immediately call that state from the redux prop binding, I do not get the current state:
import React from 'react';
import { connect } from 'react-redux';
import { setMyState } from '../actions';
class MyScreen extends React.Component {
componentWillMount() {
this.props.setMyState('value');
console.log(this.props.myState);
}
}
const mapStateToProps = state => {
const { myState } = state;
return myState;
};
export default connect(mapStateToProps, { setMyState })(MyScreen);
I understand that render will be called again when the redux state and binded props change, but what I'm trying to do is fire off another Redux action based on the resulting state. My options appear to be:
Figure out how to latest Redux state in React immediately after state change
Fire off a redux action in the reducer instead, which sounds like an anti-pattern.
Set some sort of state in my component or redux that the action should be fired on the next render, which seems clunky.

config your reducer like:
switch (action.type) {
case 'SET_STATE':
return {
...state,
loading: true,
};
case 'SET_STATE_SUCCESS':
return {
...state,
loading: false,
payload: value
};
}
and then in your component listen to
this.props.myState.loading
then trigger your action if (loading) and so on!

My solution is to use event loop handles the update, not the call stack. In particular for your case you the implementation would include setTimeout(() => {}); as the following:
import React from 'react';
import { connect } from 'react-redux';
import { setMyState } from '../actions';
class MyScreen extends React.Component {
componentWillMount() {
setTimeout(() => { this.props.setMyState('value'); },0);
setTimeout(() => { console.log(this.props.myState); },0);
}
}
const mapStateToProps = state => {
const { myState } = state;
return myState;
};
export default connect(mapStateToProps, { setMyState })(MyScreen);

What are you using this Redux state for?
If you are trying to access the data you had just set in the Redux state or if it's data that's already sitting in the Redux state you could dispatch both calls at the same time.
If you need to make the call because there is some work done to the data while setting it in the Redux state (I recommend doing this work in the action not the reducer) then componentWillReceiveProps(nextProps) will do what you need. You can compare the current props with the next props and trigger another update to the Redux state.
The best way to do this would probably be to use a Redux middleware.

Related

Unable to retrieve the state from the store after redirection in Redux React JS

I am developing a Web application using React JS + Redux. I am new to React. What I am doing now is trying to set the state on one page and then retrieve the state in another page after redirection.
I have a Component called EventListComponent that displays the list of events. Inside that component, I change the state of a reducer calling an event.
This is the reducer function I call.
import * as EditEventActions from '../actions/edit.event.actions';
export default function (state = { }, action){
switch (action.type)
{
case EditEventActions.EVENT_SET_EDITING_EVENT:
return { ...state, event: action.payload }
default:
return state;
}
}
I fire the actions before redirecting to another page like this.
this.props.setEditingEvent(event);
this.props.history.push({
pathname : '/event/'+ event.id +'/edit'
});
In the new page, I render the component called, EditEventComponent.
This is the definition of the EditEventComponent
export class EditEventComponent extends React.Component{
constructor(props)
{
super(props);
alert(this.props.event.id)//cannot retrieve event here
}
render(){
return (
<h4>This is the Edit Event component</h4>
);
}
}
function mapStateToProps(state)
{
return {
event: state.editEvent.event
};
}
function matchDispatchToProps(dispatch)
{
return bindActionCreators({
}, dispatch);
}
const enhance = compose(withWidth(), withStyles(themeStyles, { withTheme: true }), connect(mapStateToProps, matchDispatchToProps))
export default enhance(EditEventComponent);
As you can see, inside the EditEventComponent I am trying to retrieve the event field of the state which is set in the previous page. But I cannot retrieve it.
My questions are
Is the state (of the redux store) reset after redirecting to the new page?
What is wrong with my code?
If what I am doing is not the right approach, what would be the best way to pass an object from one page to another in React Redux?
Here is my action
export const EVENT_SET_EDITING_EVENT = "(EVENT) SET EDITING EVENT";
export const setEditingEvent = (data) => ({
type: EVENT_SET_EDITING_EVENT,
payload: data
});
Here is the reducer
import * as EditEventActions from '../actions/edit.event.actions';
export default function (state = { }, action){
switch (action.type)
{
case EditEventActions.EVENT_SET_EDITING_EVENT:
return { ...state, event: action.payload }
default:
return state;
}
}
I expose the EventListComponent in this way as well.
const enhance = compose(withWidth(), withStyles(themeStyles, { withTheme: true }), connect(mapStateToProps, matchDispatchToProps))
export default enhance(EventListComponent);
You are probably not setting the type correctly in setEditingEvent action and the reducer returns the initial state since it doesn't hit EVENT_SET_EDITING_EVENT

Redux is changing state of component and then it turns back to initial state

I am new Redux developer. I am trying to create a tab component using React and Redux in a web application.
When I select a tab I can in the console and in Redux Dev Tool that the state is changed, but as soon it changes it turns back to initial state (it happens to '' or 'tabData' or 'tabBulletin' in tabReducer.js
I don't understand it to solve this logical issue. Please, could anyone help me get wiser on this?
Thank you.
This is the parent React Tags related to the issue
<TabsHeader>
<TabHeader id='tab1-tab' label='Data | 34' target='tabData' />
<TabHeader id='tab2-tab' label='Bulletins | 35' target='tabBulletin' />
</TabsHeader>
<TabsContent>
</TabsContent>
TabHeader.js
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { selectTab } from './tabActions'
class TabHeader extends Component {
render() {
const selected = this.props.tab.selected === this.props.target
return (
<a href='/' id={this.props.id}
onClick={() => this.props.selectTab(this.props.target)}
data-target={this.props.target}
className={selected ? 'active' : '' }
>
{this.props.label}
</a>
)
}
}
const mapStateToProps = state => ({ tab: state.tab })
const mapDispatchToProps = dispatch => bindActionCreators({ selectTab },
dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(TabHeader)
tabActions.js
export function selectTab(tabId) {
console.log(tabId)
return {
type: 'TAB_SELECTED',
payload: tabId
}
}
tabReducer.js
const INITIAL_STATE = { selected: 'tabData' }
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'TAB_SELECTED':
return { ...state, selected: action.payload }
default:
return state
}
}
You dispatched wrong way. Where did you read this ?
dispatch => bindActionCreators({ selectTab }, dispatch)
All you need is just a simple dispatch like this:
dispatch => ({ selectTab: tabId => selectTab(tabId) })
That's is.
I am back again and now with the solution:
The problem is here on the tag of the component in TabHeader.js:
This makes the site to be reloaded turn it to initial state
So I changed to "javaxritp:;" which solves the issue with Redux.
The line below is to map action creators that allows to be trigged to send to reducers to develop state. I may loosing a small thing that makes Redux turn it back to initial state. I learnt that from my React class. Thank you anyway.
const mapDispatchToProps = dispatch => bindActionCreators({ selectTab }, dispatch)
dispatch => bindActionCreators({ selectTab }, dispatch)

Redux.js - I fail to mapStateToProps with React Redux - My React Component block on the store initial state and fail to update when store.state update

I'm trying currently to pass the app.state contained to the Redux store in a React Component.
So far, this problem is still a deep mystery...
------> HERE THE GITHUB REPOSITORY OF MY CODE <------
Hope it will help to figure out what is wrong.
Abstract :
My problem is basically about mapStateToProps, is about link a Component to the state store, AFAIK the rest work very fine, but Something seems shortcut my this.props in React's Component, because either I use connect() or delete the mapStateToProps method, my Component stil display the initial state ..!
Redux resists me like an end-level's boss...
STATE OF PLAY
The provider with a store of react-redux: OK
Connect function pass to the props: OK
mapDispatchToProps works fine! So why the state fails to update the props since the connection seems well established?
I know my action is well mapped since when I delete the mapDispatch in the connect composition, the component then fails to trigger the corresponding action.
When console.log, the mapState receive effectively the store update but the Component stay blocked on initial state (tested with a "checkState" button on the component which returns the "store.getState().propertyTargeted"
HINTS :
when I delete the mapStateToProps in connect, my React.Component continue to receive the initialState,
so maybe there is an another source that overwrites my mapStateToProps, I seek for it currently
my this.props.state variable is called in the Component's constructor, maybe the constructor doesn't receive the store.updateState or something like that ? Another track to follow.
Here my combineReducer.js :
import { combineReducers } from "redux";
import {post} from "./status"
import {entry}from "./updateState";
// only one reducer active
const appReducer = combineReducers({
entry,
post
})
export default appReducer
Here my container.js :
const mapStateToProps = (state) => {
return { word: state.entry.word }
}
const mapDispatchToProps = {
postFile: postFileAction
}
const PostFileContainer = connect(mapStateToProps, mapDispatchToProps)(Component) ;
My postFile.js :
export const postFile = (word, base64Data) => dispatch => {
console.log("postFile httpRequest reached")
dispatch({
type: 'POST_WORD',
status: request
});
Axios.post("http://localhost:7500/api/files", {
"word": word,
"data": base64Data
}, {
"Content-Type": "multipart/form-data"
})
.then(res =>
dispatch({
type: 'POST_WORD',
status: success,
res
}))
.catch(err => {
dispatch({
type: 'POST_WORD',
status: error,
err
})
});
}
Here in my store.initialState :
initial state: {
"post": {},
"entry": {
"word": "initialWord"
}
}
the UPDATE_STATE_POSTWORD is provide by an other React component therefore dispatched to the store before that the bugging component trigger it own action with a updated word's entry.
Here my UPDATE_STATE_POSTWORD action snippet :
export const updateWord = word => {
return {
type: UPDATE_STATE_POSTWORD,
word
};
}
/// reducers.js part ///
postReducer.js :
export const post = (state ={}, action) => {
console.log("postStatus reached - reducer")
switch (action.status) {
case request:
console.log("Request start")
return state
case success:
switch (action.type) {
case POST_FILE:
console.log("request succeed: ", action.res)
var _id = action.res._id
// var word= action.res.word
return (Object.assign({}, state, {
_id
}))
case POST_WORD:
console.log("request succeed: ", action.res)
return (Object.assign({}, state, {
_id: ""
}))
default :
console.log(`default state on success case in
postStatusReducer`)
return state
}
case error:
console.log("request error: ", action.err)
return state
default:
return state
}
}
entryReducer.js :
const initialState = { word : "initialWord" }
export const updateStateReducer = (state= initialState, action) =>
{
switch (action.type) {
case UPDATE_STATE_POSTWORD:
var word = action.word
return (Object.assign({}, state, {
word
}))
default:
return state
}
}
Thanks
If you are using react-thunk, your action fn would receive dispatch and getState functions as arguments.
Running getState would give you actual state of the application. Recuired data would be passed to reducer and so on.
In your example RecordingAPI receives props that comes from redux only while initializing - in constructor.
You can fix your component by adding componentWillReceiveProps method
class RecordingAPI extends React.Component {
constructor(props) {
super(props);
...
this.state = {
word : this.props.word,
state: this.props
};
}
// new method that listens to props
componentWillReceiveProps (props) {
this.setState({
word: this.props.word,
state: this.props
});
}
checkState(e){
e.persist();
e.preventDefault();
e.stopPropagation();
console.dir(this.state.word)
console.dir(this.state.state)
}
render() {
...
return (
<div>
<button onClick={(e) => this.checkState(e)}> CheckState </button>
</div>
);
}
}
My current work-around is to import the store directly in my React Component then subscribe to the changes as it :
import {store} from "../App"
store.subscribe(() => {
// When state will be updated
// we will update local component state and force component to rerender
// with new data.
this.setState({
word: store.getState().entry.word // new entry.words at each update in the statge of the React.Component
});
});
ANSWER :
Assigning the store.state value to the Component's state constructor, the Component failed to update the state. So, referring to the store.state using this.props outside any assignment to the Component.state.property works like a charm*.
The trap is that storing a props in the props.constructor.state of the children works when you work only with React.js but this mechanism doesn't works for React-Redux then you have to stay the props outside any assignment in the props.constructor.state

Redux reducers aren't updating the store

I am having bit of a wrinkle with Redux-Saga as the reducers I had done isn't updating the store as it should suppose to do.
I have used Saga to load a static in-app data and then fired the action with the payload passing the data to reducers, I did console.log() to make sure the reducers are getting the payload from action, which they are - but the problem appears to happen when I return the data into the state so that it could be accessible within the components. In props I only get default state from the reducers, any help on this matter would be highly appreciated. Following is the code I am using;
actions.js
export function loadAppAction() {
return {
type: types.LOAD_APP
}
}
export function loadAppDataAction(data) {
return {
type: types.LOAD_APP_DATA,
payload: data
}
}
api.js
import appData from '../components/appData';
export function appDataResponse() {
return appData;
}
app.js
export class App extends Component {
constructor(props) {
super(props);
const { loadAppAction } = this.props;
loadAppAction();
}
render() {
const {
initialLoadData,
activateModalAction,
deactivateModalAction,
toggleModal
} = this.props;
console.log('props', this.props)
return (
<div className="app">
{
toggleModal &&
<SignInModal
deactivateModalAction={deactivateModalAction}
/>
}
</div>
}
}
function mapStateToProps({ initialLoadReducer, toggleModalReducer }) {
console.log('lets see', initialLoadReducer);
return {
initialLoadData: initialLoadReducer,
toggleModal: toggleModalReducer
};
};
export default connect(
mapStateToProps, {
loadAppAction: actions.loadAppAction,
activateModalAction: actions.activateModalAction,
deactivateModalAction: actions.deactivateModalAction,
})
(App);
initialLoadReducers.js
export default function (state = [], action) {
switch (action.type) {
case types.LOAD_APP_DATA:
return [...action.payload];
default:
return state;
}
}
saga - index.js
function* watchLoadAppAction() {
yield takeEvery(types.LOAD_APP, loadAppSaga);
}
export default function* rootSaga() {
yield all ([watchLoadAppAction()]);
}
loadAppSaga.js
export default function* loadAppSaga(action) {
const response = yield call(api.appDataResponse);
yield put(actions.loadAppDataAction(response));
}
Following is the screenshot of my console for reference
I would suggest to call loadAppAction when componentDidMount not in the constructor. React doc also suggested the same.
https://reactjs.org/docs/react-component.html#componentdidmount
export class App extends Component {
componentDidMount() {
this.props.loadAppAction();
}
...
}
Right so, I got down to the problem here, I was trying to initialise the app with an empty array which will not going to work anyway, as the component is expecting to receive props from redux which is an empty array. Which is why, React didn't create the DOM at its first run and that caused the app to stop re-rendering even though the props are changing.
To make it work, I now initialise the app with the same data structure but with empty string values and in the next step making the data available through Redux Saga into the reducer and passing it back into the React component.

React components unmount and remount when dispatching redux action in lifecycle methods

This occurs with React + Redux Saga application my team is working on
We buy the React Isomorphic theme from Themeforest which bundles the Redux, Saga, and React Router V.4. We are now working on top of it.
I have been using React for a while but new to Redux and never experienced with such behavior. The problem is that whenever I dispatch an action, the component unmounts and remounts as the state changes
Okay, what I am doing is to fetch some user data from the API but. So this is how I come up with the following action & reducer
// Actions.js
const UserActions = {
FETCH_USER_REQUEST: 'FETCH_USER_REQUEST',
FETCH_USER_SUCCESS: 'FETCH_USER_SUCCESS',
fetch_users: () => {
return {
type: UserActions.FETCH_USER_REQUEST
}
}
}
export default UserActions;
And the reducer
// Reducer.js
export default function UserReducer(state = initialState, action) {
switch (action.type) {
case 'REQUEST_USER_SUCCESS':
return state.set('user_list', action.user_list);
default:
return state;
}
}
With Redux Saga, the middleware is created to handle async actions. This is how it looks
// Saga.js
import { all, takeEvery, call, put, fork } from 'redux-saga/effects';
import {get, post} from 'axios';
export function fetchUser() {
return get('https://mockapi.testapp.dev/users')
.then(response => {
return response.data;
}).catch(error => {
return error.data
});
}
export function* fetchUserRequest() {
yield takeEvery('FETCH_USER_REQUEST', function*() {
const resp = yield call(fetchUser);
yield put({
action: 'FETCH_USER_SUCCESS',
user_list: resp
});
});
}
export default function* rootSaga() {
yield all([
fork(fetchUserRequest)
]);
}
Now I implement the code in my component like this
// App.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import UserActions from './actions/UserAction';
const mapStateToProps = (state, ownProps) => {
return {
userList: state.User.get('user_list')
}
}
const mapDispatchToProps = dispatch => {
return {
fetchUser: () => dispatch(UserActions.fetch_users())
}
}
class App extends Component {
componentDidMount() {
this.props.fetchUser();
}
render() {
// ... The rest of the rendering processes
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Now that you can see, with the behavior mentioned prior to this. Dispatching an action via this.props.fetchUser() cause the state to change but what I don't expect is that the component shouldn't unmount and remount because once it does so, an infinite loop occurs because the componentDidMount runs over and over and state also changes accordingly.
What I expect is to fetch the data from the API once the component mounts without remounting itself once the state changes for any reason because the theme we purchased is equipped with other base components which make use of Redux-saga to handle state and async actions. For example, the collapsable sidebar triggers a dispatch which changes the state that controls its bahavior once the users click on the it. Currently, once it does that my current component immediately unmounts unexpectedly.
Is there any possible way to solve such a problem or this is the default behavior of Redux?

Categories