I'm trying to setup a redux environment and I'm stumped as to why I am having this behavior.
I have my root state here on my reducer:
export type RootState = {
counter: number;
};
const initialState: RootState = { counter: 0 };
const staticLesson = (state = initialState, action: any) => {
switch (action.type) {
case "INCREMENT":
return state.counter + 1;
default:
return state;
}
};
export default staticLesson;
I then combine it here:
import staticLesson from "./staticLesson/index";
import { combineReducers } from "redux";
const rootReducer = combineReducers({
staticLesson,
});
export default rootReducer;
I wrap my App with the Provider:
import { createStore } from "redux";
import rootReducer from "./redux/reducers";
import { Provider } from "react-redux";
import { composeWithDevTools } from "redux-devtools-extension";
const store = createStore(rootReducer, composeWithDevTools());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Yet, when I try to log it in my component, the value is undefined.
const Component = () => {
const staticLesson = useSelector((state: RootState) => state);
const dispatch = useDispatch();
console.log("counter", staticLesson.counter); <-- logs undefined
return (
...
)
}
What I don't understand is why if I just log staticLesson, it appears as expected in my console. But if I try to access the value on the object, instead of getting the value 0 for my counter ... I get undefined. staticLesson is showing up in my devTools, it's there ... I'm stumped, what am i messing up?
I'm pretty sure you should be returning the staticLesson property from the state in your useSelector call. In useSelector you're returning the entire state. So it should be:
const staticLesson = useSelector((state: RootState) => state.staticLesson);
Related
I am unable to access my state values saved in store on any screen. values reach to actions but when I access it from store it returns always undefined.
Every thing is in separate files
Reducer 1
import * as Actions from '../actionTypes'
import initialStore from './initialStore'
const homeModuleReducer = (state = initialStore, action) => {
switch (action.type) {
case Actions.SET_PROFILE_ONE:
console.log('call here')
return {
...state,
currentUser: action.profile
}
default:
return state;
}
}
export default homeModuleReducer
Reducer 2
import * as Actions from '../actionTypes'
import initialStore from './initialStore'
const loginModuleReducer = (state = initialStore, action) => {
switch (action.type) {
case Actions.SET_PROFILE:
return {
...state,
currentUser: action.profile
}
case Actions.SET_INITIAL_LOADING_STATUS:
return {
...state,
isInitialLoadingDone: action.loadingStatus
}
default:
return state;
}
}
export default loginModuleReducer
Combine Reducer
import { combineReducers } from 'redux'
import homeModuleReducer from './homeModuleReducer'
import loginModuleReducer from './loginModuleReducer'
export default combineReducers({
homeModuleReducer,
loginModuleReducer,
})
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers'
let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
export default store;
usage:
const mapStateToProps = (state) => ({
stateLoaded: state.rootReducer.isInitialLoadingDone,
profile: state.rootReducer.currentUser
});
Error:
undefined is not an object (evaluating 'state.rootReducer.isInitialLoadingDone')
You already combined your reducers so you can access reducer by it's key like this :
const mapStateToProps = (state) => ({
stateLoaded: state.homeModuleReducer.isInitialLoadingDone, // here homeModuleReducer is just an example. Change with reducer key in which isInitialLoadingDone is belong
profile: state.loginModuleReducer.currentUser
});
With hooks its much easier
wrap your root with store
import {Provider} from 'react-redux';
const App = () => {
return (
<Provider store={store}>
<Yourcomponent/>
</Provider>
);
};
export default App;
Access your state in any component like this
import { useSelector } from "react-redux";
const state = useSelector(state => state)
So I am trying to create a kind of store using react context API and I ran into a problem that when I use the useContext it is returning undefined.
So the code I have is this:
StateProvider.js
import React, { createContext, useContext, useReducer } from "react";
//Needed to track the basket and the user info
//DATA LAYER
export const StateContext = createContext();
// BUILD PROVIDER
export const StateProvider = ({ reducer, initialState, children}) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
reducer.js
export const initialState = {
basket: ["asd", "asd"],
};
function reducer(state, action) {
switch(action.type){
case 'ADD_TO_BASKET':
//add item to basket
break;
case 'REMOVE_FROM_BASKET':
//remove item from basket
break;
default:
return state;
}
}
export default reducer;
index.js
import reducer, { initialState } from './state/reducer';
....
<StateProvider initalState={initialState} reducer={reducer}>
<App />
</StateProvider>
And the problem is on this file, where I try to console log the basket that I get from the reducer.js initalState.
Header.js
import { useStateValue } from '../state/StateProvider'
...
function Header() {
console.log(useContext(StateContext))
const [{ basket }]= useStateValue();
console.log(basket);
So the error is when I use the const [{ basket }]= useStateValue();, it says this : Cannot read property 'basket' of undefined.
The problem was on index.js, initialState was badly written and I was getting no error because of ES6.
The State
The tracing results
Each time when I click the button, the dispatch run twice, like the picture above.
This is the AppBar component and mapDispatchToProps function.
const mapStateToProps = state => {
return {
title: state.title
};
};
const mapDispatchToProps = {
onClick: () => {
return {
type: "TOGGLE_SIDEBAR"
};
}
};
const AppBar = props => (
<Box>
<Button icon={<Notification />} onClick={props.onClick} />
</Box>
);
const AppBatContainer = connect(
mapStateToProps,
mapDispatchToProps
)(AppBar);
export default AppBatContainer;
This is the reducer
import {
TOGGLE_SIDEBAR
} from "../constants/action-types";
const initialState = {
showBar: false
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_SIDEBAR:
return Object.assign({}, state, {
showBar: !state.showBar
});
default:
return state;
}
};
export default rootReducer;
This is the store.js
import { createStore, applyMiddleware, compose } from "redux";
import reduxThunk from "redux-thunk";
import rootReducer from "./reducers/index";
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
trace: true,
traceLimit: 20
})
: compose;
const enhancer = composeEnhancers(applyMiddleware(reduxThunk));
const store = createStore(rootReducer, enhancer);
export default store;
All libraries are well imported. I tried remove redux-thunk, it is still the same result.
Thanks in advance.
Ref
React-Redux Counter example action firing twice I have viewed this questions on stackoverflow, and tried the answers, but no of them solved it.
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!
Why am I getting TypeError: notes.map is not a function in the following part of my Notes component? {notes.map((note) => (
components/Notes.js
import React, { Component } from "react"
import { connect } from "react-redux"
const mapStateToProps = (state) => {
return { notes: state.notes }
}
const NotesList = ({ notes }) => (
<ul className="notes_list">
{notes.map((note) => (
<li className="note_body" key={note.id}>
<div dangerouslySetInnerHTML={{ __html: note.body }}></div>
</li>
))}
</ul>
)
const Notes = connect(mapStateToProps)(NotesList);
export default Notes;
reducers/notes.js
import * as types from '../actions/actionTypes'
const initialState = {
notes: [{id: 1, body: "hey"}]
}
function notes(state = initialState, action) {
switch (action.type) {
...
default:
return state
}
}
export default notes
root reducer
import { combineReducers } from 'redux'
import notes from './notes'
import noteForm from './noteForm'
const rootReducer = combineReducers({
notes,
noteForm
})
export default rootReducer
app.js
import React, { Component } from 'react';
import Notes from './components/Notes'
import NoteForm from './components/NoteForm'
const App = () => (
<div className="App">
<NoteForm />
<Notes />
</div>
)
export default App
---upd
store
import { createStore, applyMiddleware } from 'redux'
import rootReducer from '../reducers'
import {ping} from './enhancers/ping'
import thunk from 'redux-thunk'
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(thunk, ping))
return store
}
index.js
...
import configureStore from './store/configureStore'
const store = configureStore()
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
Are you providing the connect function with a store? If so, everything looks fine to me -- it'd be useful to see your store initialization code.
Create a store with createStore from redux and wrap your App with a Provider from react-redux:
app.js
...
import notesReducer from './reducers/notes'
import { createStore } from 'redux'
const store = createStore(notesReducer) // use combineReducers when you add a 2nd reducer
const App = () => (
<Provider store={store}>
<div className="App">
<NoteForm />
<Notes />
</div>
</Provider>
)
If you already have a Provider somewhere else, check if everything's okay there.
Here's my fully working example - I copied your Notes.js file and wrote up the following App.js - no errors whatsoever (I bundled store creation and reducers all in one file for simplicity):
import React, { Component } from 'react';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import Notes from './Notes'
const initialState = {
notes: [{
id: 1,
body: 'testing'
}]
}
function notes(state = initialState, action) {
switch (action.type) {
default:
return state
}
}
const store = createStore(notes)
export default () => (
<Provider store={store}>
<Notes />
</Provider>
)
Update for combineReducers
When using combineReducers, your reducers' initialState will already be namespaced in the store under the key which was used in the combineReducers call. Change your notes reducer's initialState to an array:
import * as types from '../actions/actionTypes'
// no need for { notes: [] } here, combineReducers({ notes }) will take care of that
const initialState = [{ id: 1, body: 'hey' }]
function notes(state = initialState, action) {
switch (action.type) {
...
default:
return state
}
}
export default notes
When you get map isn't a function that means you're not calling the data correctly.
I see in notes reducer page you're not calling the states correctly
function notes(state = initialState, action) {
switch (action.type) {
...
default:
return state
}
}
Change it to:
function notes(state = initialState.notes, action) {
switch (action.type) {
...
default:
return state
}
}
The regular way to do this is to not putting your states in an array
const initialState = {
id: 1,
body: "hey"
}
function notes(state = initialState, action) {
switch (action.type) {
...
default:
return state
}
}
This will works fine
since my root reducer has the following structure
const rootReducer = combineReducers({
notes
})
I can reach notes by state.notes.notes
const mapStateToProps = (state) => {
return { notes: state.notes.notes }
}
having the following initial state structure for notes
const initialState = {
notes: []
}