My component is not rerendering after the store is changing.
I make sure that the store is actually changing by dropping him to the console with
store.subscribe() and console.log(store.getState()) but still the component is not rerendering again.
I will appreciate your help.
configureStore.js
import { createStore, combineReducers } from 'redux';
import home from '../reducers/home';
import favorites from '../reducers/favorites';
export default () => {
const store = createStore(combineReducers({
home,
favorites
}))
return store;
}
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './redux/store/configureStore';
import AppRouter from './router/AppRouter';
const store = configureStore();
const jsx = (
<Provider store={store}>
<AppRouter />
</Provider>
);
ReactDOM.render(jsx, document.querySelector('#root'));
home.js (reducer)
const homeDefaultState = {
name: 'someName'
}
export default (state = homeDefaultState, action) => {
switch (action.type) {
case 'CHANGE_NAME':
return {
...state,
name: 'otherName'
}
default:
return state;
}
}
home.js (action)
export const changeName = () => ({
type: 'CHANGE_NAME'
})
Home.js (component)
import React from 'react';
import configureStore from '../../redux/store/configureStore';
import { changeName } from '../../redux/actions/home';
import { connect } from 'react-redux';
const store = configureStore();
const handleName = () => {
store.dispatch(changeName())
}
const Home = (props) => (
<div className="home">
<button onClick={handleName}>
change name
</button>
{props.home.name}
</div>
);
const mapStateToProps = (state) => ({
home: state.home
});
export default connect(mapStateToProps)(Home);
In your Home component you initialize store for second time. And bound action to this second store
const store = configureStore();
const handleName = () => {
store.dispatch(changeName())
}
At the same time with connect() you access store declared in App.jsx
Read from first but update second. Just remove second store and use mapDispatchToProps(second parameter passed to connect()) instead:
const mapStateToProps = (state) => ({
home: state.home
});
export default connect(mapStateToProps, { handleName: changeName })(Home);
Related
I have started learning react-redux and was trying out the same but somehow the props are getting returned as undefined after mapping. Sharing code flow:
Below is the detailed code giving a brief idea on each component used.
App.js:
import './App.css';
import CakeContainer from './redux/cakes/CakeContainer';
import React from 'react';
import { Provider } from 'react-redux';
import store from './redux/store';
function App() {
console.log(store.getState())
return (
<Provider store = {store}>
<div className="App">
<CakeContainer/>
</div>
</Provider>
);
}
export default App;
CakeContainer.js
import React from 'react'
import { connect } from 'react-redux'
import { buyCake } from './CakeAction'
function CakeContainer(props) {
return (
<div>
<h1>Cake Container !!</h1>
<h2>Number of cakes - {props.cake}</h2>
<button onClick = {props.buyCake}> buy cakes</button>
</div>
)
}
const mapStateToProps = (state) =>{
return {
cake: state.cakeQuant
}
}
const mapDispatchToProps = dispatch => {
return {
buyCake: () => dispatch(buyCake())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CakeContainer)
Action.js
import {BUY_CAKE} from './CakeTypes'
export const buyCake = () =>{
return{
type: 'BUY_CAKE'
}
}
Reducer:
Reducer where i am passing the initial state and action for further processing.
import { BUY_CAKE } from "./CakeTypes";
const initialState = {
cakeQunt : 10
}
const CakeReducer = (state = initialState, action)=>{
switch(action.type){
case 'BUY_CAKE': return {
...state,
cakeQunt: state.cakeQunt - 1
}
default: return state;
}
}
export default CakeReducer;
Store.js
Creating store and passing reducer details to it
[![import { createStore } from "redux";
import CakeReducer from './cakes/CakeReducer'
const store = createStore(CakeReducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
console.log(store.getState())
export default store;][1]][1]
Image showing the props value as undefined:
[1]: https://i.stack.imgur.com/EjXU1.png
My toolbar component dispatches an action abortGame(). I see in the console that it reaches the action creator (text "ONE!" displayed on the console) but never the reducer (text "TWO!" never displayed).
What is wrong?
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import App from './App';
import reducer from './store/reducers/reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
));
const app = (
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
ReactDOM.render(app, document.getElementById('root'));
reducer.js
import * as actionTypes from '../actions/actionTypes';
const initialState = {
score: 0
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ABORT_GAME:
console.log('TWO');
return {
...state,
score: 0,
};
default:
return state;
}
};
export default reducer;
actionTypes.js
export const ABORT_GAME = 'ABORT_GAME';
actions.js
import * as actionTypes from './actionTypes';
export const abortGame = () => {
console.log('ONE!');
return {
type: actionTypes.ABORT_GAME
};
};
Toolbar.js (component)
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { abortGame } from '../../store/actions/actions';
const toolbar = (props) => (
<div>
<div onClick={props.onAbortGame}><Link to="/">MyApp</Link></div>
</div>
);
const mapDispatchToProps = dispatch => {
return {
onAbortGame: () => dispatch(abortGame)
};
};
export default connect(null, mapDispatchToProps)(toolbar);
You have just missed the function brackets while dispatching the action
const mapDispatchToProps = dispatch => {
return {
onAbortGame: () => dispatch(abortGame)
};
};
Just change this line onAbortGame: () => dispatch(abortGame) to onAbortGame: () => dispatch(abortGame())
abortGame is a action creator which is basically a function. So you need to call the function inside dispatch
You just passing the function name in dispatch not calling it exactly.
Just convert the dispatch(abortGame())
sandbox working link
I am new to react and redux and trying to dispatch states from store but unable to do so,
Please anyone help me to resolve the problem...
App.js
import React from "react";
import store from './reduxStore/store';
import { Provider } from 'react-redux';
import Sidebar from './Sidebar';
function App() {
return (
<div>
<Provider store={store}>
<Sidebar ></Sidebar>
</Provider>
</div>
);
}
export default (App);
This is my sidebar.js Component
import React, { useEffect } from 'react';
import { updatePageLinkActions } from "../../reduxStore/actions/updatePageLinkActions";
import { connect } from "react-redux";
const Sidebar = () =>{
useEffect(() => {
return updatePageLinkActions
}, [])
return (
<>
<ListItem button onClick={updatePageLinkActions}>
<ListItemIcon><Home></Home></ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button onClick={() => handleOpenPage("contact")}>
<ListItemIcon><AcUnit></AcUnit></ListItemIcon>
<ListItemText primary="Contact" />
</ListItem>
</>
)
}
const mapStateToProps = (state) => ({
updatePage: state.updatePage.pgLink
})
export default connect(mapStateToProps, { updatePageLinkActions })(SidebarItems);
Store Store.js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleWare = [thunk]
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleWare),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
)
)
export default store;
Root Reducer rootReducer.js
import { combineReducers } from 'redux';
import updatePageLinkReducer from './updatePageLinRreducer';
export default combineReducers({
updatePage: updatePageLinkReducer
});
Action updatePageLinkActions.js
import { UPDATE_PAGE_LINK } from "../actionTypes";
//import store from "../store";
export const updatePageLinkActions = dispatch => {
console.log("actions log ", dispatch.payload)
return dispatch({
type: UPDATE_PAGE_LINK,
payload: {pageLink : "Action_pageLink" }
})
};
Reducer updatePageLinkReducer.js
import { UPDATE_PAGE_LINK } from '../actionTypes';
const initialState = {
pgLink: []
};
// eslint-disable-next-line import/no-anonymous-default-export
export default function(state = initialState, action) {
switch (action.type) {
case UPDATE_PAGE_LINK:
console.log("Action called ")
return { ...state, pgLink: action.payload }
default:
return state;
}
}
I am unable to dispatch values from store,
Please somebody help me...
If you are using functional components I strongly recommend getting rid of using connect.
To get state from Redux store, you can use useSelector hook, and to dispatch an action, you can use useDispatch hook.
import { useDispatch, useSelector } from 'react-redux'
...
const dispatch = useDispatch()
const updatePage = useSelector(state => state.updatePage.pgLink)
const updatePageLinkActions = () => {
dispatch({
type: UPDATE_PAGE_LINK,
payload: {pageLink : "Action_pageLink" }
})
}
...
Your action creator is not defined properly.
It should be this:
export const updatePageLinkActions = () => dispatch => {
console.log("actions log ", dispatch.payload)
return dispatch({
type: UPDATE_PAGE_LINK,
payload: {pageLink : "Action_pageLink" }
})
};
i am learning redux with react and trying to create an app where i have a range-slider, whose value dictates how many Box components will be rendered on the screen.
i am trying to make the range-slider a controlled component but can't make it change the store. i am getting no errors.
the component:
import React from 'react';
import { connect } from 'react-redux';
import { setBoxNumber } from '../actions/actions';
const Slider = ({ boxNumber, handleChange }) => {
return(
<div>
<div>
{boxNumber}
</div>
<div>
<input
onChange={handleChange}
value={boxNumber}
type="range"
min="12"
max="480"
/>
</div>
</div>
)
}
const mapStateToProps = (state) => {
return { boxNumber: state.boxNumber }
}
const mapDispatchToProps = (dispatch) => {
return {
handleChange: (event) => dispatch(setBoxNumber(event.target.value))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Slider);
the reducer:
import { combineReducers } from 'redux';
export const boxNumberReducer = (boxNumber = 40, action) => {
switch(action.payload) {
case 'SET_BOX_NUMBER':
return action.payload;
default:
return boxNumber;
}
}
export default combineReducers({
boxNumber: boxNumberReducer
})
the action:
export const setBoxNumber = (number) => {
return {
type: 'SET_BOX_NUMBER',
payload: number
}
}
i also tried to call the handleChange method with an arrow function on change, like i would do with a controlled react component without redux, but it's making no difference
I think your reducer is configured incorrectly. You can pass all the initial states inside the variable initialState like this.
//reducer.js
import { combineReducers } from "redux";
const initialState = {
boxNumber: 40,
};
const boxReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_BOX_NUMBER":
return {
...state,
boxNumber: action.payload,
};
default:
return state;
}
};
export default combineReducers({
boxReducer,
});
This is how your index.js file should look like:
//index.js
import React from "react";
import ReactDOM from "react-dom";
import Slider from "./Slider.js";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducer from "./redux/reducer";
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<Slider />
</Provider>,
document.getElementById("root")
);
You need to update your mapStateToProps in Slider.js to access the states in your reducer.
//Slider.js
const mapStateToProps = (state) => {
return { boxNumber: state.boxReducer.boxNumber };
};
This is a simple fix. As your app gets bigger, you'll need more reducers and thus it's better to keep a separate file for that.
When console.log'ing state or attempting to render in component, my store returns as undefined. However, in React devtools, the store is showing as expected.
index.js with dummy dispatch calls
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { addItem } from "./actions/actions";
import configureStore from "./store/configure-store";
import DashboardPage from "./components/DashboardPage";
const store = configureStore();
store.dispatch(addItem({ description: "item 1" }));
store.dispatch(addItem({ description: "item 2" }));
store.dispatch(addItem({ description: "item 3" }));
const jsx = (
<Provider store={store}>
<DashboardPage />
</Provider>
);
ReactDOM.render(jsx, document.getElementById('root'));
config-store.js
import { createStore } from 'redux';
import reducer from "../reducers/reducer";
export default () => {
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
return store;
};
DashboardPage.js
import React from "react";
import { connect } from "react-redux";
const DashboardPage = (props) => {
console.log(props.items); // for debugging
return (
<div>
<h1>Items:</h1>
<p>{props.items}</p>
</div>
)
};
const mapStateToProps = (state) => {
return { items: state.items };
};
export default connect(mapStateToProps)(DashboardPage);
reducer.js
const reducerDefaultState = [];
export default (state = reducerDefaultState, action) => {
switch (action.type) {
case 'ADD_ITEM':
return [
...state,
action.item
];
default:
return state;
}
};
actions.js
export const addItem = (description = "") => ({
type: 'ADD_ITEM',
item: { description }
});
I have studied many resources online relating to this issue, however I can't see where I am differing from suggested implementation.
Your state is an Array which doesn't have items property.
Try using this code:
const mapStateToProps = (state) => {
return { items: state };
};
Or change your reducer/createStore to something like this
import { createStore, combineReducers } from 'redux';
import reducer from "../reducers/reducer";
export default () => {
const store = createStore(
combineReducers({items: reducer}),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
return store;
};
https://redux.js.org/api/combinereducers