ES6 modules initializing the wrong order? - javascript

So I have a tree of modules, and two in particular that are behaving oddly. Using Babel 6 + Browserify (babelify).
Module #1 (WindowManager/index.js) exports a named function called reducer, as well as a default export.
import WindowManager from './component';
import { combineReducers } from 'redux';
import { connect } from 'react-redux';
import { reducer as search_palette } from './SearchPalette';
const initialState = {
// stuff
};
const window_manager = (state = initialState, action) => {
// stuff
return state;
};
export const reducer = combineReducers({window_manager, search_palette})
const mapStateToProps = (state) => {
return {
// stuff
};
};
export default connect(mapStateToProps)(WindowManager)
Module #2 (store.js) imports WindowManager/index.js's reducer function and tries to use it.
import { createStore, combineReducers } from 'redux';
import { reducer as WindowManagerReducer } from './WindowManager';
const initialState = {
// stuff
};
const FetchApplicationsReducer = (state = initialState, action) => {
// stuff
return state;
}
export default createStore(
combineReducers({
applications: FetchApplicationsReducer,
ui: WindowManagerReducer,
})
)
For some reason, store.js is getting executed before WindowManager/index.js and thus the exports are not available.
Anyone run into this?

After much tweaking, modifying the order of import statements fixed the issue. It's a complex tree, so hard to represent here in a way that doesn't involving dumping the whole project... but I think ultimately there may be a bug inside Babel's ES6 module system somewhere.

Related

How to share data between two slice reducers redux toolkit

I have two slice reducers and I want to get the state of one to the other.
In the old way of redux I would do it like that:
import store from "store"
///reducer code
const state = store.getState();
Now Iam using redux-toolkit and this way of importing store doesnt work.
here is LoginSlice:
import { createSlice } from '#reduxjs/toolkit';
import axios from "axios"
import Swal from 'sweetalert2'
export const initialState = {
id: "1111"
}
export const LoginSlice = createSlice({
name: 'login',
initialState,
reducers: {
login: state => {
state.id = "2222"
},
},
});
export const { login} = LoginSlice.actions;
here is Sites slice :
import { createSlice } from '#reduxjs/toolkit';
import axios from "axios"
export const SitesSlice = createSlice({
name: 'sites',
initialState: {
sites: []
},
reducers: {
GetSites: (state,action) => {
axios.post(process.env.REACT_APP_API_URL+"/sites/get",{id: get id here}).then((err,data) =>{
if(data.data.success){
state.sites = data.data.data
}else{
error
}
})
.catch(error => {
error
})
}
},
});
export const { GetSites } = SitesSlice.actions;
here is the store:
import { configureStore } from '#reduxjs/toolkit';
import { combineReducers } from 'redux'
import loginReducer from '../features/Login/loginSlice';
import siteReducer from '../features/Sites/SitesSlice';
const reducer = combineReducers({
login: loginReducer,
sites: siteReducer,
})
export default configureStore({
reducer
});
what I want to do is to get the user id from the logIn Slice to the Sites Slice.
something like store.getState().logIn.user.id
I am having trouble with this );
You should never do any side effect within a reducer - this includes both accessing an external variable like state (as suggested by Muhammad Yaqoob in his answer) as well as executing a axios.post like you are trying there. (Also, that is not only something that should not be done - it will also never work)
This should be done in a middleware or just in a thunk. Since that is a whole new can of worms that goes well beyond the scope of this question, please see the official redux tutorial on this topic: https://redux.js.org/tutorials/essentials/part-5-async-logic
(If you follow this path you will notice that you can access current state values from within middleware, thunks and createAsyncThunk, which should then also answer your question)
The configureStore() function returns the same store object which you can simply get like this:
const store = configureStore({
reducer
})
And then use it, like you normally would.
store.getState()
You might want to export store also.
export default store;

Getting 'import/no-anonymous-default-export' warning for named functions in React

I'm working with a React / Redux app that for some reason is getting a 'import/no-anonymous-default-export' warning for the app reducers even though the reducer functions are named.
userRedirect.js
import {SET_USER_REDIRECT} from "../actions/actionTypes";
const initialState = {
active: false,
title: '',
messages: [],
btnText: ''
};
const userRedirect = (state = initialState, action) => {
switch (action.type) {
case SET_USER_REDIRECT:
let newState = {...state};
newState = {...newState, ...action.payload.redirectData};
return newState;
default:
return state;
}
};
export default userRedirect;
This reducer is being imported into a index.js that is located in a reducers folder where userRedirect.js is also located. It then uses combineReducers method from redux to handle the reducers.
import {combineReducers} from 'redux';
import userData from './userData';
import userRedirect from './userRedirect';
export default combineReducers({
userData,
userRedirect
});
This is then imported into the index of a store folder where this happens:
import {createStore, applyMiddleware} from "redux";
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from "./../reducers";
export default createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
To me it seems like there is a error with the plugin or something, but I've tried npm install, I've tried creating the userRedirect reducer using a function declaration and not a expression, but no matter what I do it keeps showing the warning and for the life of me I cannot figure out why.
Do you guys have any ideas or suggestions in regards to this? Am I just missing something?
Much appreciate it!
[
export default combineReducers({
userData,
userRedirect
});
export default createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
These two are anonymous export.
Change it to
const rootReducer = combineReducers({
userData,
userRedirect
});
export default rootReducer;
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
export default store;
Forgive me if does not look good ... answering from mobile.
Not able to format from mobile

react redux applyMiddleware is not a function

Couldn't spot any flaw in my code myself, I got error of
Uncaught TypeError: (0 , _reactRedux.applyMiddleware) is not a
function
import { applyMiddleware, createStore } from 'react-redux'
import { promiseMiddleware } from './middleware'
const defaultState = {
appName: 'conduit',
}
const reducer = (state = defaultState, action) => {
switch(action.type) {
case 'HOME_PAGE_LOADED':
return { ...state, articles: action.payload.articles }
}
return state
}
const middleware = applyMiddleware(promiseMiddleware)
const store = createStore(reducer, middleware)
export default store
Any clue what's wrong?
You've got your imports mixed up. createStore and applyMiddleware are part of the main Redux package, not React-Redux. You need:
import {createStore, applyMiddleware} from "redux";
You need to call promise middleware like this
const middleware = applyMiddleware(promiseMiddleware())
i.e- Use function brackets after promiseMiddleware text.
See documentation here-

React-Redux - No reducer provided for key "coins"

Not sure why I'm getting the following errors.
I'm just setting up my store, actions and reducers, I haven't called dispatch on anything yet.
Expected
App runs fine, Redux state is not updated
Results
src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
import App from './App'
import css from './coinhover.scss'
const element = document.getElementById('coinhover');
const store = createStore(reducer, compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>, element);
src/reducer/index.js
import { combineReducers } from 'redux'
import { coins } from './coins'
export default combineReducers({
coins
});
src/reducer/actions/coins.js
import * as api from '../../services/api'
import { storage, addToPortfolio } from '../../services/coinFactory'
export const ADD_COIN = 'ADD_COIN'
export function getCoin(coin) {
return dispatch => {
api.getCoin(coin)
.then((res_coin) => addToPortfolio(res_coin))
.then((portfolio) => dispatch(updatePortfolio(portfolio)));
}
}
export function updatePortfolio(portfolio) {
return {
type: ADD_COIN,
portfolio
}
}
finally src/reducer/coins/index.js
import { ADD_COIN } from './actions'
const initialState = [];
export default (state = initialState, action) => {
switch(action.type) {
case ADD_COIN:
return action.portfolio;
default:
return state;
}
}
Your issue lies with how you're importing your coins reducer:
import { coins } from './coins'
The latter tries to obtain a named export returned from the file in ./coins.
You are not using any named exports only export default, therefore you just need to import the file as follows:
import coins from './coins';
Using the latter will result with the fact that coins will then contain the value of export default; which will be the coins reducer.
Even when all your imports are correctly imported, this can still happen for one other reason. Circular Dependency!
In my case, this happeded because of a circular dependency in a file. I had two circular dependecy in the project that I created accedentally. Example: rootReducer.ts -> authSlice.ts -> rootReducer.ts.
These dependencies are often not as easy to debug. I used this package to check for circular dependencies. Once the circular dependency was removed, all was well.
Ah just found it, I was importing my coins reducer incorrectly...
import { combineReducers } from 'redux'
import coins from './coins' // because I have coins/index.js
export default combineReducers({
coins
});
instead of
import { coins } from './coins'
This was my fix:
import { combineReducers } from 'redux'
import { coins } from './coins'
export default combineReducers({
coinsState: coins || (() => null) // By adding this I resolved it.
});
If nothing worked for you this might be a circular dependency. I had a project like that requiring store state in the slice. Instead of using the store there was a thunk available that can provide you with the state without importing the store itself. If you have this edge case you can get state from thunk.
thunkAPI.getState()
In my case, I was not adding a default property to my reducers. When I added it, it worked.
here is my code;
const counterReducer = (state = 0, action) => {
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
export default counterReducer;
and combinde file;
import counter from './counter';
import { combineReducers } from 'redux';
const allReducers = combineReducers({
counter: counter,
});
export default allReducers;

Mixing two stores into single, common one - ReactJS

I have my app (a boilterplate with few, my own features). It has a global store (build-in, came from the boilerplate), which looks like:
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; //combine reducers from redux form
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';
import { reducer as reduxFormReducer } from 'redux-form'; //registration form
const sagaMiddleware = createSagaMiddleware();
export default function configureStore(initialState = {}, history) {
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [
sagaMiddleware,
routerMiddleware(history),
];
const enhancers = [
applyMiddleware(...middlewares),
];
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers =
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose;
/* eslint-enable */
const store = createStore(
createReducer(),
fromJS(initialState),
composeEnhancers(...enhancers)
);
// Extensions
store.runSaga = sagaMiddleware.run;
store.asyncReducers = {}; // Async reducer registry
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept('./reducers', () => {
import('./reducers').then((reducerModule) => {
const createReducers = reducerModule.default;
const nextReducers = createReducers(store.asyncReducers);
store.replaceReducer(nextReducers);
});
});
}
return store;
}
Few days ago I've implemented a redux-form (it works great), but unfortunately it has an own, local store, which is not compatibile with the global one and looks like:
import { createStore, combineReducers } from 'redux';
import { reducer as reduxFormReducer } from 'redux-form';
const reducer = combineReducers({
form: reduxFormReducer
});
const store = (window.devToolsExtension
? window.devToolsExtension()(createStore)
: createStore)(reducer);
export default store;
As far as I know, the store has to be global - the first one is, but the second one (for redux form) isn't.
I would like to ask you
How to mix these two stores into a single, common, global one?
Thank you for any answers!
Edit: Redux form comes from: https://codesandbox.io/s/qx95rm7gG
React redux comes from: https://github.com/react-boilerplate/react-boilerplate
Edit2: Files hierarchy:
-app
--index.js
--store.js (first one, global)
--containers
---UserRegistration
----index.js
----store.js (the second one, local)
but unfortunately it has an own, local store, which is not compatibile with the global one and looks like
This is not correct. The redux-form reducer is a standard reducer like any other and just needs to be combined into your reducer (from import createReducer from './reducers';).
If you are already combining reducers in createReducer (I assume you are because of the store.asyncReducers in the hot reloading), then you just need to include the reduxFormReducer with form as the key, something like:
import { combineReducers } from 'redux';
import { reducer as reduxFormReducer } from 'redux-form';
import someReducer from './someReducer';
import someOtherReducer from './someOtherReducer';
export default function createReducer(asyncReducers = {}) {
return combineReducers({
form: reduxFormReducer,
someReducer,
someOtherReducer,
...asyncReducers
});
}
If you share ./reducers/index.js I can make this answer more specific.

Categories