Unable to access Reducer & state values - javascript

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)

Related

Redux: useSelector value is undefined despite it appearing in devTools

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);

unable to receive the state in react component from redux

I am trying to display the redux state into my react component, but it comes undefined.
I am unable to understand where am I doing the mistake.
I am learning redux by trying a coding on my own by going through the redux documentation.
Main React component
import React, { Component } from 'react';
import Counter from './components/Counter';
import {Provider} from 'react-redux';
import store from './redux/store';
class App extends Component {
render() {
return (
<Provider store={store}>
<div>
<h1>COUNTER APPlICATION</h1>
<Counter />
</div>
</Provider>
)
}
}
export default App;
React Component
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {addNumber} from '../redux/actions/addAction';
import {substractNumber} from '../redux/actions/substractAction';
export class Counter extends Component {
render() {
return (
<div>
<h1>Value:{this.props.value}</h1>
<h1>Add Only Value:{this.props.addOnly}</h1>
<button onClick = {() => this.props.addNumber}>+</button>
<button onClick = {() => this.props.substractNumber}>-</button>
</div>
)
}
}
const mapStateToProps = state => ({
value: state.value
});
export default connect(mapStateToProps, {addNumber, substractNumber})(Counter);
addReducer
import {ADDITION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch(action.type){
case ADDITION:
return{
value: state.value + 2
}
default:
return state
}
}
substractReducer
import {SUBSTRACTION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch (action.type) {
case SUBSTRACTION:
return {
value: state.value - 2
}
default:
return state
}
}
rootReducer
import {combineReducers} from 'redux';
import addReducer from './addReducer';
import substractReducer from './substractReducer';
export default combineReducers({
add: addReducer,
substract: substractReducer
})
store
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers/rootReducer';
import thunk from 'redux-thunk';
export default createStore(rootReducer, applyMiddleware(thunk));
action type
export const ADDITION = 'ADDITION';
export const SUBSTRACTION = 'SUBSTRACTION';
addAction
import {ADDITION} from './actionTypes';
export const addNumber = () => (dispatch) => {
return dispatch({
type: ADDITION,
payload: 2
})
}
substractAction
import {SUBSTRACTION} from './actionTypes';
export const substractNumber = () => (dispatch) => {
return dispatch({
type: SUBSTRACTION,
payload: 2
})
}
You are doing wrong.
you state is just counter value, so don't split into two reducers. You only need two case statement, one for ADD, one for SUBTRACT.
Don't use combineReducer and it you want, use one key like counter for counter reducer
in mapStateToProp, get value like state.counter.value where counter is name of key you used in combineReducer({ counter: counterReducer })
Your button actions/onclick is wrong
import {ADDITION, SUBTRACTION} from '../actions/actionTypes';
const initialState = {
value: 50
}
export default function (state = initialState, action) {
switch(action.type){
case ADDITION:
return
value: state.value + 2
}
case SUBTRACTION:
return{
value: state.value + 2
}
default:
return state
}
}
///// no need to regester 2 reducer, just add one above like this
export default combineReducers({
counter: counterReducer
});
/// In Counter component , mapStateToProp
const mapStateToProps = state => ({
value: state.counter.value
});
// Just pass redux actions to onClick in button like this
<button onClick = {this.props.addNumber}>+</button>
<button onClick = {this.props.substractNumber}>-</button>
When you combineReducers like this:
export default combineReducers({
add: addReducer,
substract: substractReducer
})
Your state tree will look like:
{
add: {
value: 0
},
subtract: {
value: 0
}
}
So you should only have a single reducer in order to reduce over the same value.

Redux Thunk for simple async requests

I am building a simple app to retrieve some recipes from an API URL.
I am reading the documentation of Thunk to implement it but I cannot understand how to set the async get request.
What is strange is that if I console.log the action passed into the reducer it definitely retrieves the correct object (a list of recipes for shredded chicken).
When I pass the action onto the the reducer, instead, it throws the error:
"Unhandled Rejection (Error): Given action "FETCH_RECIPES", reducer "recipes" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined."
Is there any error in my action creator? is the API call properly done?
Store
import React from 'react';
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import SearchBar from './components/App';
import thunk from 'redux-thunk';
import 'bootstrap/dist/css/bootstrap.css';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
render(
<Provider store={store}>
<SearchBar />
</Provider>,
document.getElementById('root')
)
Component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './App.css';
import { Button } from 'reactstrap';
import { Form } from 'reactstrap';
import { bindActionCreators } from 'redux';
import {fetchRecipe } from '../actions';
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = { term: ''};
this.typeRecipe = this.typeRecipe.bind(this)
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(e) {
e.preventDefault()
this.props.fetchRecipe(this.state.term)
}
typeRecipe(e) {
this.setState({term: e.target.value});
}
render() {
return (
<div className="SearchBar">
<Form onSubmit={this.onFormSubmit}>
<input type='text'
value={this.state.term}
placeholder='ciao'
onChange={this.typeRecipe}
/>
<br/>
<Button id='ciao' className='btn-success'>Submit</Button>
</Form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchRecipe }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchBar);
Action creator
import axios from 'axios';
export const FETCH_RECIPES = 'FETCH_RECIPES';
const API_KEY = 'xxx';//not the real one.
export function fetchRecipe() {
const request = axios.get(`http://food2fork.com/api/search?key=${API_KEY}&q=shredded%20chicken`);
return (dispatch) => {
request.then(({data}) =>{
dispatch({ type: FETCH_RECIPES, payload: data})
})
}
}
reducer
import { FETCH_RECIPES } from '../actions';
export default function (state = {}, action) {
switch(action.type) {
case FETCH_RECIPES:
const newState = action.payload.data;
return newState;
default:
return state
}
}
combineReducer (index)
import recipeReducer from '../reducers/recipes_reducer';
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
recipes: recipeReducer
});
export default rootReducer;
Mistake is in the reducer return statement.
export default function (state = {}, action) {
switch(action.type) {
case FETCH_RECIPES:
const newState = {...state, data : action.payload.data};
return newState;
default:
return state
}
}
Here we are adding data key to the reducer state, to access this you can use do this in you container :
export default connect((state)=>{
var mapStateToProps = {};
if(state.recipes.data) {
mapStateToProps['recipes'] = state.recipes.data
}
return mapStateToProps;
}, mapDispatchToProps)(SearchBar);
and recipes data will be available as this.props.recipes.

React Native Redux Reducer not working

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!

react-redux TypeError: notes.map is not a function

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: []
}

Categories

Resources