I am a novice in Javascript and Redux, and I just created my first app based on this tutorial. I have a problem that the Store absolutely doesn't react to any dispatch call. I don't know if there is an error in the connection between Redux and React or in the store configuration itself.
Could you please help me with this problem?
This is snippes from my actions file where "addTodo" action is defined.
export const ADD_TODO = 'ADD_TODO';
let todoId = 0;
export const addTodo = (text) => ({
type: ADD_TODO,
id: todoId++,
text,
});
Below is my Store configuration.
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers';
import DevTools from '../containers/DevTools';
const enhancer = compose(
applyMiddleware(createLogger),
DevTools.instrument()
);
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, enhancer);
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
My index file where I try to call a dispatch function with "AddTodo" action creator. Similarly, I call this function in Redux containers, but it doesn't work for both.
import React from 'react';
import { render } from 'react-dom';
import configureStore from './store/configureStore';
import { addTodo } from './actions';
const store = configureStore();
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch(addTodo('test'));
The whole project is placed on Github too. I will be thankful if you help me.
you forgot to invoke createLogger, try
const enhancer = compose(
applyMiddleware(createLogger())
...
);
webpackbin test
Related
I am Creating a React-app and using Reduxjs Toolkit for state management. I am not able to understand why ChannelID and Channelname are undefined here.
Here is the App.js
import { useSelector } from 'react-redux'
import { selectChannelId, selectChannelname } from '../../reduxjs toolkit/appSlice'
const Chats = () => {
const user = JSON.parse(localStorage.getItem("user"))
const data = localStorage.getItem("userData");
const [userData, setUserData] = useState(JSON.parse(localStorage.getItem("userData")))
const ChannelId = useSelector(selectChannelId) // Giving Error Here
const channelName = useSelector(selectChannelname) // Giving Error Here
Here is the appSlice
import { createSlice } from "#reduxjs/toolkit";
export const appSlice = createSlice({
"name":'app',
initialState:{
channelId:null,
channelName:null,
},
reducers:{
setChannelInfo: (state,action)=>{
state.channelId = action.payload.channelId
state.channelName = action.payload.channelName
},
}
})
export const {setChannelInfo}= appSlice.actions
export const selectChannelId = (state) => state.app.channelId
export const selectChannelname = (state) => state.app.channelName
export default appSlice.reducer;
Error Message
Code for Store
import { createStore } from "redux";
import reducers from "./reducers"
const store = createStore(reducers, {}, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default store;
Use configureStore to create your store, this is "a friendly abstraction over the standard Redux createStore function that adds good defaults to the store setup for a better development experience." quoted from the rtk doc.
For your case it will be as such
import appReducer from './%%WhereYourSliceResides
import { configureStore } from '#reduxjs/toolkit'
export const store = configureStore({
reducer: {
app: appReducer,
},
})
Then the selector will pick up state.app from your store.
I'm studying Redux with React Native and I have an example like this:
reducer.js
import {combineReducers} from "redux";
import {HomeReducer as home} from "../routes/Home/modules/home";
const makeRootReducer = () => {
return combineReducers({
home
});
};
export default makeRootReducer;
createStore.js
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from "redux-thunk";
import {createLogger} from 'redux-logger';
import makeRouteReducer from './reducer';
const log = createLogger({diff: true, collapsed: true});
export default (initialState = {}) => {
const middleware = [thunk, log];
const enhancers = [];
return store = createStore(
makeRouteReducer(),
initialState,
compose(
applyMiddleware(...middleware),
...enhancers
)
);
}
It works fine but I don't understand why I have to use export default in reducer.js.
When I try to use
// reducer.js
export makeRootReducer
and
// createStore.js
import {makeRouteReducer} from './reducer';
It didn't work.
Please help me by explaining it in detail.
Thank you very much.
You're talking about named exports and default exports...
Example of a named export:
export const someFunction = () => {
// some code here...
}
Now you can import this in another file like this:
import {someFumnction} from './nameOfFile'
But if you do:
export default function someFunction () {
// some code here...
}
That's a default export and you have to import it like this:
import someFunction from './nameOfFile'
In your example if you change this:
const makeRootReducer = () => {
return combineReducers({
home
});
};
export default makeRootReducer;
To this:
export const makeRootReducer = () => {
return combineReducers({
home
});
};
It will become a named export and not a default export and now you can do:
import {makeRootReducer} from....
Hope that clarifies... And here is more info
I'm attempting to implement redux into a relatively simple app, however my actions don't seem to be triggering the reducers properly. Through console logging the action seems to be firing, but the respective reducer isn't being executed.
App.js:
import {Provider} from 'react-redux';
import configureStore from './src/config/configureStore.js';
const store = configureStore();
export default class App extends React.Component {
render() {
return (
<Provider store = {store}>
<RootStack />
</Provider>
);
}
}
configureStore.js:
import {createStore, applyMiddleware} from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const store = createStore (
reducers,
applyMiddleware(thunk)
);
return store;
}
actions/index.js:
export const saveRisk = (payload) => {
console.log('saved RISK!');
return (dispatch) => {
dispatch({type: 'risk_chosen',payload: payload});
}
}
reducers/index.js:
import { combineReducers } from 'redux';
import RiskReducer from './RiskReducer';
export default combineReducers({
risk_level: RiskReducer
});
RiskReducer.js
const INITIAL_STATE = {risk_level: false};
export default (risk = INITIAL_STATE, action) => {
if(action.type === 'risk_chosen') {
console.log('RISK REDUCER SUCCESSFUL')
return {
...risk, risk_level: action.payload
};
}
console.log('REDUCER RISK:');
console.log(risk);
return risk;
}
RiskTolerance.js (A child component within RootStack which is using redux):
import { connect } from 'react-redux';
import {saveRisk} from '../../actions'
#connect(state => ({risk_level: state.risk_level.risk_level}, {saveRisk}))
export default class RiskTolerance extends React.Component {
// ...
componentDidMount(){
console.log(this.props.risk_level);
// ^^returns undefined, despite the reducer initializing it to "false"
let riskVal = 'something'
this.props.saveRisk(riskVal)
}
// ...
}
EDIT: I have changed the initial value in the reducer to an appropriate object but my reducer is still not working after the action is called. Any ideas?
Thank you!
There is problem with initial state in your reducer. Make changes as shown below:
INITIAL_STATE = { risk_level: false }
Figured it out, when calling the action I needed to write:
this.props.dispatch(this.props.saveRisk(riskVal))
Thanks for your help everyone!
can't persist store to localstorage, using redux-persist.
Have error:
Store does not have a valid reducer. Make sure the argument passed to
combineReducers is an object whose values are reducers.
Help pls to configure store or are there another ways
import {applyMiddleware, compose, createStore} from "redux";
import thunk from "redux-thunk";
import {createLogger} from "redux-logger";
import rootReducer from "../reducer/index";
import {loadState, saveState} from "../utils";
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const config = {
key: 'root',
storage,
}
const reducer = persistCombineReducers(config, rootReducer)
function configureStore(initialState) {
let createStoreWithMiddleware;
const middleware = process.env.__DEV__
? applyMiddleware(thunk, createLogger())
: applyMiddleware(thunk);
createStoreWithMiddleware = compose(
middleware,
);
const store = createStoreWithMiddleware(createStore)(reducer, initialState);
let persistor = persistStore(store)
if (module.hot) {
module.hot
.accept('../reducer', () => {
const nextRootReducer = require('../reducer/index');
store.replaceReducer(nextRootReducer);
});
}
store.subscribe(() => {
saveState(store.getState().albums)
});
return {store, persistor};
}
export default configureStore
As the error message is already telling - you have to pass an object to the persistCombineReducers method.
Please have a look at the following example. (Some of your code is commented there to have a minimal example.)
So the code of your combined reducer could be like this:
const reducer = persistCombineReducers(config, {root: rootReducer});
const store = createStore(reducer, initialState);
If you're only having one reducer it would be better to use:
const reducer = persistReducer(config, rootReducer);
Here is my index.js where I initially dispatch an action to read my list of locations:
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import configureStore from './store/configureStore';
import {Provider} from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import {loadLocationList} from './actions/locationActions';
import './css/styles.css';
const store = configureStore();
render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>,
document.getElementById('app')
);
Then here is my action where I get the data & then create an action out of it:
export function loadLocationListSuccess(alistingData) {
return { type: types.LOAD_LOCATION_LIST_SUCCESS, listingData: alistingData};
}
export function loadLocationList() {
return function(dispatch){ //we return a function that accepts a parameter, we just called it dispatch
//dispatch(fetchCallActions.fetchCallStart("")); // we dispatch a function fetchCallStart to indicate the start of our call, this is to keep in check with our asynchronous function calls
let link = 'http://example.com:8399/location';//our fetch url
console.log(link); //we log our link, just for debug purposes
return fetch(link) //start fetch
.then(function(response) {
return response.json();
}).then(function(json) {
dispatch(loadLocationListSuccess(json));
}).catch(function(ex) {
console.log('parsing failed', ex);
});
};
}
Then here is my reducer:
import * as types from '../actions/actionTypes';
export default function locationReducer(state = [], action) {
switch(action.type) {
case types.LOAD_LOCATION_LIST_SUCCESS:
return {listingData: action.listingData};
default:
return state;
}
}
Then here is my mapStateToProps & connect function:
function mapStateToProps(state, ownProps) {
return {
// we'll call this in our component -> this.props.listingData
listingData: state.listingData
};
}
export default connect(mapStateToProps, mapDispatchToProps)(homePage);
For some reason, it cannot read state.listingData or am I actually doing it wrongly? Anyone can help me with this problem?
I tried logging state.listingData and it showed undefined
Here is my configureStore:
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../reducers';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk, reduxImmutableStateInvariant())
);
}
Here is my combined Reducer:
import {combineReducers} from 'redux';
import courses from './courseReducer';
import locations from './locationReducer';
const rootReducer = combineReducers({
courses,
locations
});
export default rootReducer;
Did I not connect it to the store properly?
Recent update:
Logging JSON.stringify(state) in mapStateToProps would actually shows the result. Thanks guys.
The correct path turned out to be state.locations.listingData because I think in my combined Reducer I included the reducer as locations so maybe thats why the state for it is state.locations. Hope this helps anyone with the problem.
Can you show the code of configureStore file? The problem might be there, may be you forgot to add reducer to list of reducers.
Does the action works right? Did you log data before dispatch(loadLocationListSuccess(json));?
UPD:
Because of rootReducer. Each reducer creates their own key in store. When you combine your reducers in rootReducer, like:
import locations from './locationReducer';
const rootReducer = combineReducers({
courses,
locations
});
It creates store with this kind of structure:
const store = {
courses: {},
locations: {}
}
So, after that you dispatched action and reducer changed the data to this:
const store = {
courses: {},
locations: {
listingData: someData
}
}
If you want to access to listingData like: state.listingData, you need to change a little your reducer and combineReducer to:
export default function listingData(state = {}, action) {
switch(action.type) {
case types.LOAD_LOCATION_LIST_SUCCESS:
return action.listingData;
default:
return state;
}
}
...
import listingData from './locationReducer';
const rootReducer = combineReducers({
courses,
listingData
});