Parameter 'initialState' cannot be referenced in its initializer - javascript

In my ReactJS / Typescript app, I'm getting the following error in my store.ts:
Parameter 'initialState' cannot be referenced in its initializer.
interface IinitialState {
fiatPrices: [];
wallets: [];
defaultCurrency: string;
}
const initialState = {
fiatPrices: [],
wallets: [],
defaultCurrency: ''
}
...
export function initializeStore (initialState:IinitialState = initialState) {
return createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
Anyone else run into this issue? Currently having to rely on // #ts-ignore
Entire store.ts file:
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
interface IinitialState {
fiatPrices: [];
wallets: [];
defaultCurrency: string;
}
const initialState = {
fiatPrices: [],
wallets: [],
defaultCurrency: ''
}
export const actionTypes = {
GET_PRICES: 'GET_PRICES'
}
// REDUCERS
export const reducer = (state = initialState, action: any) => {
switch (action.type) {
case actionTypes.GET_PRICES:
return state
default:
return state
}
}
// MOCK API
export async function getProgress(dispatch: any) {
try {
const priceList = await fetchPrices();
return dispatch({ type: actionTypes.GET_PRICES, payload: priceList })
}
catch (err) {
console.log('Error', err);
}
}
// Wait 1 sec before resolving promise
function fetchPrices() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ progress: 100 });
}, 1000);
});
}
// ACTIONS
export const addLoader = () => (dispatch: any) => {
getProgress(dispatch);
}
// #ts-ignore
export function initializeStore (initialState:IinitialState = initialState) {
return createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
withReduxStore lib file
import React from 'react'
import { initializeStore, IinitialState } from '../store'
const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'
function getOrCreateStore (initialState: IinitialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return initializeStore(initialState)
}
// Create store if unavailable on the client and set it on the window object
// Waiting for (#ts-ignore-file) https://github.com/Microsoft/TypeScript/issues/19573 to be implemented
// #ts-ignore
if (!window[__NEXT_REDUX_STORE__]) {
// #ts-ignore
window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
}
// #ts-ignore
return window[__NEXT_REDUX_STORE__]
}
// #ts-ignore
export default App => {
return class AppWithRedux extends React.Component {
// #ts-ignore
static async getInitialProps (appContext) {
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore()
// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore
let appProps = {}
if (typeof App.getInitialProps === 'function') {
appProps = await App.getInitialProps(appContext)
}
return {
...appProps,
initialReduxState: reduxStore.getState()
}
}
// #ts-ignore
constructor (props) {
super(props)
this.reduxStore = getOrCreateStore(props.initialReduxState)
}
render () {
return <App {...this.props} reduxStore={this.reduxStore} />
}
}
}

function initializeStore (initialState:IinitialState = initialState) { ... }
is not valid by any means, not just in TypeScript. It's incorrect to suppress the error with #ts-ignore.
initialState parameter shadows the variable of the same name from enclosing scope, so default parameter value refers the parameter itself. This will result in discarding default parameter value with ES5 target and in an error with ES6 target.
The parameter and default value should have different names:
function initializeStore (initialState:IinitialState = defaultInitialState) { ... }
Notice that the use of defaultInitialState isn't needed in a reducer, due to how initial state works. Initial state from createStore takes precedence if combineReducers is not in use.

Related

Server Error Error: Invalid hook call. Hooks can only be called inside of the body of a function component in _app.js

I am a newbie in React and Next JS, I want to set initial auth user data on initial load from the __app.js. But using dispatch throwing error "Invalid hook call". I know according to docs calling hooks in render function is wrong. but I am looking for an alternate solution to this.
How I can set auth data one-time so that will be available for all the pages and components.
I am including my code below.
/contexts/app.js
import { useReducer, useContext, createContext } from 'react'
const AppStateContext = createContext()
const AppDispatchContext = createContext()
const reducer = (state, action) => {
switch (action.type) {
case 'SET_AUTH': {
return state = action.payload
}
default: {
throw new Error(`Unknown action: ${action.type}`)
}
}
}
export const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, {})
return (
<AppDispatchContext.Provider value={dispatch}>
<AppStateContext.Provider value={state}>
{children}
</AppStateContext.Provider>
</AppDispatchContext.Provider>
)
}
export const useAuth = () => useContext(AppStateContext)
export const useDispatchAuth = () => useContext(AppDispatchContext)
/_app.js
import 'bootstrap/dist/css/bootstrap.min.css'
import '../styles/globals.css'
import App from 'next/app'
import Layout from '../components/Layout'
import { mutate } from 'swr'
import { getUser } from '../requests/userApi'
import { AppProvider, useDispatchAuth } from '../contexts/app'
class MyApp extends App {
render() {
const dispatchAuth = useDispatchAuth()
const { Component, pageProps, props } = this.props
// Set initial user data
const setInitialUserData = async () => {
if (props.isServer) {
const initialData = {
loading: false,
loggedIn: (props.user) ? true : false,
user: props.user
}
const auth = await mutate('api-user', initialData, false)
dispatchAuth({
type: 'SET_AUTH',
payload: auth
})
}
}
//----------------------
// Set initial user data
setInitialUserData()
//----------------------
return (
<AppProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</AppProvider>
)
}
}
MyApp.getInitialProps = async (appContext) => {
let isServer = (appContext.ctx.req) ? true : false
let user = null
let userTypes = {}
// Get user server side
if (isServer) {
await getUser()
.then(response => {
let data = response.data
if (data.status == true) {
// Set user
user = data.data.user
userTypes = data.data.user_types
//---------
}
})
.catch(error => {
//
})
}
//---------------------
return {
props: {
user,
userTypes,
isServer
}
}
}
export default MyApp
I believe this is the intended use of the useEffect hook with an empty array as its second argument:
https://reactjs.org/docs/hooks-effect.html
import {useEffect} from 'react'
class MyApp extends App {
useEffect(()=> {
setInitialUserData()
},[])
render() {
...
}
}

getPastEvents undefined(re)

I had asked this question before.
I looked carefully at the exchange section, which I advised, and I think there is no problem with the exchange section.
At least in my opinion there is no problem and I hardly know the cause of the problem.
And I was so frustrated that I put everything in the code.
If anyone can provide us with a clue to this problem, please reply to me.
interactions.js
import Web3 from 'web3'
import {
web3Loaded,
web3AccountLoaded,
tokenLoaded,
exchangeLoaded,
cancelledOrdersLoaded
} from './actions'
import Token from '../abis/Token.json'
import Exchange from '../abis/Exchange.json'
export const loadWeb3 = (dispatch) => {
const web3 = new Web3(Web3.givenProvider || 'http://localhost:7545')
dispatch(web3Loaded(web3))
return web3
}
export const loadAccount = async (web3, dispatch) => {
const accounts = await web3.eth.getAccounts()
const account = accounts[0]
dispatch(web3AccountLoaded(account))
return account
}
export const loadToken = async (web3, networkId, dispatch) => {
try {
const token = new web3.eth.Contract(Token.abi, Token.networks[networkId].address)
dispatch(tokenLoaded(token))
return token
} catch (error) {
console.log('Contract not deployed to the current network. Please select another network with Metamask.')
return null
}
}
export const loadExchange = async (web3, networkId, dispatch) => {
try {
const exchange = new web3.eth.Contract(Exchange.abi, Exchange.networks[networkId].address)
dispatch(exchangeLoaded(exchange))
return exchange
} catch (error) {
console.log('Contract not deployed to the current network. Please select another network with Metamask.')
return null
}
}
export const loadAllOrders = async (exchange, dispatch) => {
// if (exchange) { // Make sure exchange has been defined
// const exchange = new web3.eth.Contract(Exchange.abi, Exchange.networks[networkId].address)
const cancelStream = await exchange.getPastEvents('Cancel', { fromBlock: 0, toBlock: 'latest' })
// // await loadAllOrders(this.props.exchange, dispatch)
console.log(cancelStream)
}
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Navbar from './Navbar'
import Web3 from 'web3';
import Content from './Content'
import { connect } from 'react-redux'
// import Token from '../abis/Token.json'
import {
loadWeb3,
loadAccount,
loadToken,
loadExchange
} from '../store/interactions'
import { contractsLoadedSelector } from '../store/selectors'
class App extends Component {
componentWillMount() {
this.loadBlockchainData(this.props.dispatch)
}
async loadBlockchainData(dispatch) {
const web3 = loadWeb3(dispatch)
const network = await web3.eth.net.getNetworkType()
const networkId = await web3.eth.net.getId()
const accounts = await loadAccount(web3, dispatch)
const token = await loadToken(web3, networkId, dispatch)
if(!token) {
window.alert('Token smart contract not detected on the current network. Please select another network with Metamask.')
return
}
const exchange = await loadExchange(web3, networkId, dispatch)
if(!exchange) {
window.alert('Exchange smart contract not detected on the current network. Please select another network with Metamask.')
return
}
}
render() {
return (
<div>
<Navbar />
{ this.props.contractsLoaded ? <Content /> : <div className="content"></div> }
</div>
);
}
}
function mapStateToProps(state) {
return {
contractsLoaded: contractsLoadedSelector(state)
}
}
export default connect(mapStateToProps)(App);
reducers.js
import { combineReducers } from 'redux';
function web3(state={}, action) {
switch (action.type) {
case 'WEB3_LOADED':
return { ...state, connection: action.connection }
case 'WEB3_ACCOUNT_LOADED':
return { ...state, account: action.account }
default:
return state
}
}
function token(state = {}, action) {
switch (action.type) {
case 'TOKEN_LOADED':
return { ...state, loaded: true, contract: action.contract }
default:
return state
}
}
function exchange(state = {}, action) {
switch (action.type) {
case 'EXCHANGE_LOADED':
return { ...state, loaded: true, contract: action.contract }
case 'CANCELLED_ORDERS_LOADED':
return { ...state, cancelledOrders: { loaded: true, data: action.cancelledOrders } }
// case 'FILLED_ORDERS_LOADED':
// return { ...state, filledOrders: { loaded: true, data: action.filledOrders } }
// case 'ALL_ORDERS_LOADED':
// return { ...state, allOrders: { loaded: true, data: action.allOrders } }
default:
return state
}
}
const rootReducer = combineReducers({
web3,
token,
exchange
})
export default rootReducer
Content.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { exchangeSelector } from '../store/selectors'
import { loadAllOrders } from '../store/interactions'
class Content extends Component {
componentWillMount() {
this.loadBlockchainData(this.props.dispatch)
}
// async loadBlockchainData(exchange, dispatch) {
async loadBlockchainData(dispatch) {
await loadAllOrders(this.props.exchange, dispatch)
// this.loadBlockchainData(this.props.exchange)
// await loadAllOrders(exchange, dispatch)
}
function mapStateToProps(state) {
return {
exchange: state.exchangeSelector
}
}
export default connect(mapStateToProps)(Content)
selectors.js
import { get } from 'lodash'
import { createSelector } from 'reselect'
const account = state => get(state, 'web3.account')
export const accountSelector = createSelector(account, a => a)
const tokenLoaded = state => get(state, 'token.loaded', false)
export const tokenLoadedSelector = createSelector(tokenLoaded, tl => tl)
const exchangeLoaded = state => get(state, 'exchange.loaded', false)
export const exchangeLoadedSelector = createSelector(exchangeLoaded, el => el)
const exchange = state => get(state, 'exchange.contract')
export const exchangeSelector = createSelector(exchange, e => e)
export const contractsLoadedSelector = createSelector(
tokenLoaded,
exchangeLoaded,
(tl, el) => (tl && el)
)
Check exchange to make sure not undefined
export const loadAllOrders = async (exchange, dispatch) => {
const cancelStream = exchange ?
await exchange.getPastEvents('Cancel', { fromBlock: 0, toBlock: 'latest' })
: null // Check if exchange defined then call getPastEvents
console.log(cancelStream)
}

React router - not getting parameter from url

I have a route to a component HandlingIndex:
<Route strict path={handlingCasePath} component={HandlingIndex} />
HandlingIndex is wrapped with a trackRouteParam component. trackRouteParam component looks like this:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { parseQueryString } from '../../utils/urlUtils';
const defaultConfig = {
paramName: '',
parse: a => a,
paramPropType: PropTypes.any,
storeParam: () => undefined,
getParamFromStore: () => undefined,
isQueryParam: false,
paramsAreEqual: (paramFromUrl, paramFromStore) => paramFromUrl === paramFromStore
};
/**
* trackRouteParam
*
* Higher order component that tracks a route parameter and stores in the application
* state whenever it changes.
* #param config
*/
const trackRouteParam = config => (WrappedComponent) => {
class RouteParamTrackerImpl extends Component {
constructor() {
super();
this.updateParam = this.updateParam.bind(this);
}
componentDidMount() {
this.updateParam();
}
componentDidUpdate(prevProps) {
this.updateParam(prevProps.paramFromUrl);
}
componentWillUnmount() {
const { storeParam } = this.props;
storeParam(undefined);
}
updateParam(prevParamFromUrl) {
const { paramFromUrl, storeParam, paramsAreEqual } = this.props;
if (!paramsAreEqual(paramFromUrl, prevParamFromUrl)) {
storeParam(paramFromUrl);
}
}
render() {
const {
paramFromUrl,
paramFromStore,
storeParam,
paramsAreEqual,
...otherProps
} = this.props;
return <WrappedComponent {...otherProps} />;
}
}
const trackingConfig = { ...defaultConfig, ...config };
RouteParamTrackerImpl.propTypes = {
paramFromUrl: trackingConfig.paramPropType,
paramFromStore: trackingConfig.paramPropType,
storeParam: PropTypes.func.isRequired,
paramsAreEqual: PropTypes.func.isRequired
};
RouteParamTrackerImpl.defaultProps = {
paramFromUrl: undefined,
paramFromStore: undefined
};
const mapStateToProps = state => ({ paramFromStore: trackingConfig.getParamFromStore(state) });
const mapDispatchToProps = dispatch => bindActionCreators({ storeParam: trackingConfig.storeParam }, dispatch);
const mapMatchToParam = (match, location) => {
const params = trackingConfig.isQueryParam ? parseQueryString(location.search) : match.params;
return trackingConfig.parse(params[trackingConfig.paramName]);
};
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
...ownProps,
...stateProps,
...dispatchProps,
paramFromUrl: mapMatchToParam(ownProps.match, ownProps.location),
paramsAreEqual: trackingConfig.paramsAreEqual
});
const RouteParamTracker = withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(RouteParamTrackerImpl));
RouteParamTracker.WrappedComponent = WrappedComponent;
Object.keys(RouteParamTracker).forEach((ownPropKey) => {
RouteParamTracker[ownPropKey] = WrappedComponent[ownPropKey];
});
return RouteParamTracker;
};
export default trackRouteParam;
In the component HandlingIndex, I am trying to get a param caseNumber from the url. Just showing the relevant parts here from the component:
const mapStateToProps = state => ({
selectedCaseNumber: getSelectedCaseNumber(state)
});
export default trackRouteParam({
paramName: 'caseNumber',
parse: caseNumberFromUrl => Number.parseInt(caseNumberFromUrl , 10),
paramPropType: PropTypes.number,
storeParam: setSelectedCaseNumber,
getParamFromStore: getSelectedCaseNumber
})(connect(mapStateToProps)(requireProps(['selectedCaseNumber'])(HandlingIndex)));
Action creator for the setSelectedCaseNumber is:
export const setSelectedCaseNumber= caseNumber=> ({
type: SET_SELECTED_CASE_NUMBER,
data: caseNumber
});
So, when I am going to the route 'case/1234', where the parameter is caseNumber: 1234 where I am setting the selectedCaseNumber I see that the data field is NaN. On inspecting the console, I can see that I in the function:
const mapMatchToParam = (match, location) => {
const params = trackingConfig.isQueryParam ? parseQueryString(location.search) : match.params;
return trackingConfig.parse(params[trackingConfig.paramName]);
};
I can see that match.params is an empty object.
I am not sure why is that, why I am getting an empty object?
In trackRouteParam HOC,
At line:
const RouteParamTracker = withRouter(connect(mapStateToProps, mapDispatchToProps, mergeProps)(RouteParamTrackerImpl));
You try edit:
const RouteParamTracker = connect(mapStateToProps, mapDispatchToProps, mergeProps)(withRouter(RouteParamTrackerImpl));
Hope can help you!

reactjs redux Actions must be plain objects

I have a problem "Actions must be plain objects. Use custom middleware for async actions."
I'm using reactjs with this boilerplate (https://github.com/react-boilerplate/react-boilerplate/)
I waste 1 day to fix this problem, but no result. I was trying move fetch request to action (without saga) and result the same.
My component:
...
import { compose } from 'redux';
import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga';
import { successFetching } from './actions';
import reducer from './reducers';
import saga from './saga';
class NewsListPage extends React.PureComponent {
componentDidMount() {
const { dispatch } = this.props
dispatch(saga())
}
...
};
NewsListPage.propTypes = {
isFetching: PropTypes.bool.isRequired,
isSuccess: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
}
const selector = (state) => state;
const mapStateToProps = createSelector(
selector,
(isFetching, isSuccess, items) => ({ isFetching, isSuccess,items })
);
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withReducer = injectReducer({ key: 'NewsList', reducer });
const withSaga = injectSaga({ key: 'NewsList', saga });
const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(
withReducer,
withSaga,
withConnect
)(NewsListPage);
My actions:
export const NEWS_FETCH_LOADING = 'NEWS_FETCH_LOADING';
export const NEWS_FETCH_FAILURE = 'NEWS_FETCH_FAILURE';
export const NEWS_FETCH_SUCCESS = 'NEWS_FETCH_SUCCESS';
export function preFetching() {
return {
type: NEWS_FETCH_LOADING,
}
}
export function successFetching(json) {
return {
type: NEWS_FETCH_SUCCESS,
payload: json
}
}
export function failureFetching(error) {
return {
type: NEWS_FETCH_FAILURE,
payload: error
}
}
My reducers:
...
import { NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS } from './actions'
const INITIAL_STATE = {
isFetching: false,
isSuccess: false,
items: []
};
function NewsListReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case NEWS_FETCH_LOADING:
case NEWS_FETCH_FAILURE:
return Object.assign({}, state, {
isFetching: true,
isSuccess: false
})
case NEWS_FETCH_SUCCESS:
return Object.assign({}, state, {
isFetching: false,
isSuccess: true,
items: action.payload,
})
default:
return Object.assign({}, state)
}
}
export const rootReducer = combineReducers({
NewsListReducer
})
export default rootReducer
My saga:
import { call, put, select, takeLatest } from 'redux-saga/effects';
import {NEWS_FETCH_LOADING, NEWS_FETCH_FAILURE, NEWS_FETCH_SUCCESS, preFetching, successFetching, failureFetching} from './actions';
import request from 'utils/request';
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put(successFetching(req));
} catch (err) {
yield put(failureFetching(err));
}
}
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
EDIT:
#Alleo Indong, i tried your advice and its almost work.
I change in component mapDispatchToProps to
export function mapDispatchToProps(dispatch) {
return {
getData: () => dispatch(loadNews()),
};
}
And add new function to actions.js
export function loadNews() {
return {
type: NEWS_FETCH_SUCCESS,
}
}
But now ajax sent every seconds like in while cycle. I tried call this.props.getData(); in componentDidMount and in constructorand result same.
EDIT 2
In component i add
import * as actionCreators from './actions';
import { bindActionCreators } from 'redux';
In constructor i change
constructor(props) {
super(props);
const {dispatch} = this.props;
this.boundActionCreators = bindActionCreators(actionCreators, dispatch)
}
But here dispatch is undefined and in componentDidMount too.
And change mapDispatchToProps to
export function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
Hello #AND and welcome to stackoverflow! As I mentioned in the comment on the main post, you are dispatching a GENERATOR instead of an object on your
dispatch(saga());
Here's an example to help you
On your component import the actions that you want to use like this.
import * actionCreators from './actions';
import { bindActionCreators } from 'redux';
Learn more about bindActionCreators here enter link description here
This will import all of the exported actionCreators that you created there.
In my opinion you don't need successFetching and failureFetching anymore as you can dispatch this actions later on on your saga
Then in your mapDispatchToProps you would want to register this actioncreator
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
};
}
Then on your saga, I want to point up some problems here as well.
First your function
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_SUCCESS, getNews);
}
Is actually what we call a watcher where it watch when a certain action was dispatch, in this function you are already waiting for the NEWS_FETCH_SUCCESS before getNews is called which is wrong, because you first have do the FETCHING before you will know if it is failed or success so yeah this function should be like this
export default function* NewsListSaga() {
yield takeLatest(NEWS_FETCH_LOADING, getNews);
}
This simple means that you will take all the latestNEWS_FETCH_LOADINGactions that was dispatched and will call thegetNews`.
then on your getNews generator function, you can do it like this
export function* getNews() {
const requestURL ='%MY_URL%';
try {
const req = yield call(request, requestURL);
yield put({type: NEWS_FETCH_SUCCESS, req});
} catch (err) {
yield put({type: NEWS_FETCH_FAILED, err});
}
}
in here
const req = yield call(request, requestURL);
You are saying that you will wait for the result of the request / service that you called, it might be a promise.
Then in here, this is why you won't need the functions successFetching and failureFetching functions anymore, since you can do it like this
yield put({type: NEWS_FETCH_SUCCESS, req});
One last important step that you have to do now is to call the actionCreator inside your componentDidMount()
like this
componentDidMount() {
const { preFetching } = this.props;
preFetching();
}

Redux Persist: some part of state not being persisted

I'm using redux with redux-persist and redux-thunk.
Only some part of state is not being persisted. what could be the reason? is it a way to force persist the current state? When I call reducers, state gets updated. but when I reload the app, some part of state is still the old one. (empty)
I thought it is taking some parts of state from initialstate, so I added some entries to initialstate, even so, it's returning empty objects. not getting them from initialState.
Thanks in advance.
only discussionStudents gets persisted
Store setup:
import React from "react";
import { View, AsyncStorage } from 'react-native';
import { applyMiddleware, createStore, compose } from 'redux';
import { Provider } from 'react-redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import { reducer } from './reducers';
import Container from './Container';
const middlewares = [thunk];
const logger = createLogger();
middlewares.push(logger);
const store = createStore(reducer, compose(
applyMiddleware(...middlewares)
), autoRehydrate({ log: true }));
persistStore(store, {storage: AsyncStorage});
const Root = () => (
<Provider store={store}>
<Container />
</Provider>
);
export default Root;
parts of the reducer :
import {REHYDRATE} from 'redux-persist/constants';
export const types = {
SYNCHRONISE_DISCUSSIONS: 'SYNCHRONISE_DISCUSSIONS'
};
export const actionCreators = {
synchroniseDiscussions: (args) => {
return dispatch => {
/// Call API
synchroniseDiscussionsAPI()
.then((res) => {
return dispatch(synchroniseDiscussions(res))
})
.catch((e) => {
console.log(e)
})
}
}
}
const synchroniseDiscussions = (args) => {
return {type: types.SYNCHRONISE_DISCUSSIONS, payload: args}
}
const initialState = {
rehydrated: false,
discussionStudents: [],
discussionGroups: [],
discussionsMessages: [],
discussionParticipants: []
}
export const reducer = (state = initialState, action) => {
const {
discussionStudents,
discussionGroups,
discussionsMessages,
discussionParticipants
} = state;
const {type, payload} = action;
switch (type) {
case types.SYNCHRONISE_DISCUSSIONS:
{
const oldStudents = discussionStudents
const newStudents = payload.discussionStudents
var parsedStudents = []
oldStudents.forEach((old, i)=>{
if(newStudents.findIndex(newstd => newstd.userId == old.userId) < 0){
parsedStudents.push({
...old,
status: 'inactive'
})
}
})
newStudents.forEach((newStudent, i)=>{
if(parsedStudents.findIndex(pstd => pstd.userId == newStudent.userId) < 0){
parsedStudents.push({
...newStudent,
status: 'active'
})
}
})
var newdiscussionParticipants = payload.discussionParticipants
var newdiscussionGroups = payload.discussionGroups
return Object.assign({}, state, {
discussionStudents: parsedStudents,
discussionParticipants: newdiscussionParticipants,
discussionGroups: newdiscussionGroups
})
}
case REHYDRATE:
{
return {
...state,
rehydrated: true
}
}
}
return state
}
I've found the issue. Issue was that I was using for loops inside the function. and promise was being resolved before the loop finishes. Issue solved by replacing the built in javascript loop with a custom asynchronous loop :
const asyncLoop = (iterations, func, callback) => {
var index = 0;
var done = false;
var loop = {
next: function() {
if (done) {
return;
}
if (index < iterations) {
index++;
func(loop);
} else {
done = true;
callback();
}
},
iteration: function() {
return index - 1;
},
break: function() {
done = true;
callback();
}
};
loop.next();
return loop;
}

Categories