react-redux state is changing but display is stills same (no re-render) - javascript

i've been working on a project trying to learn redux with react. But there is an error and i don't know exactly how to fix it. Files/codes in down if you need more information about how store works.
store/index.js
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "./reducers";
import thunk from 'redux-thunk'
const middlewares = [thunk]
const store = createStore(rootReducer, compose(applyMiddleware(...middlewares), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))
export default store;
actions/index.js
import axios from "axios"
export const increment = (payload) => {
return {
type: 'INCREMENT',
payload: payload
}
}
export const decrement = () => {
return {
type: 'DECREMENT'
}
}
export const fetch = () => {
return async (dispatch) => {
axios.get('https://jsonplaceholder.typicode.com/posts/')
.then(data => dispatch({type: 'FETCH', payload: data.data}))
}
}
store/todos.js
const todos = (state = [], action) => {
switch(action.type){
case 'FETCH':
return Object.assign(state, action.payload);
default:
return state;
}
}
export default todos;
App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch, connect} from 'react-redux'
import {increment, decrement, fetch} from './store/actions/'
import { GeistProvider, CssBaseline } from '#geist-ui/react';
function App(props) {
const count = useSelector((state) => state.counter)
const todos = useSelector((state) => state.todos)
const dispatch = useDispatch()
return (
<div className="App">
<header className="App-header">
<h1>Count is {count}</h1>
<button onClick={() => dispatch(increment(3))}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(fetch())}>FETCH</button>
{todos.length ? todos[0].title : <h1>Not fetched.</h1>}
</header>
</div>
);
}
export default App;
This is the codes in project. Let me know if you guys need more information about anything. Thanks for help!

You are wrong at return Object.assign(state, action.payload);. It's mutated state so redux can't detect state change. You should read this https://redux.js.org/understanding/thinking-in-redux/three-principles#changes-are-made-with-pure-functions
You can change to this
return Object.assign({}, state, action.payload);
or this
return { ...state, ...action.payload }

Related

React in Redux not effect Dispatch() method onclick on Add button

Add.js
'+' button on click to not effect in state.value
import { useDispatch } from "react-redux"
import { AddNumber } from "./action/index";
export const Add=()=>{
const dispatch = useDispatch();
return(
<>
<button onClick={() => dispatch(AddNumber())}>+</button>
</>
)
}
action->index.js
export const AddNumber =()=>{
return{
type:"increment"
}
}
Reducer->index.js
import ChangeNumber from "./UpDown";
import { combineReducers} from "redux"
const rootReducer = combineReducers({
counter: ChangeNumber
})
export default rootReducer;
Reducer->updown.js
const initialState ={value:0};
const ChangeNumber = (state=initialState,action) =>{
switch(action.type){
case 'increment': return state.value + 1;
case 'decriment': return state.value - 1;
default:return state;
}
}
export default ChangeNumber;
Store.js
import { configureStore } from '#reduxjs/toolkit'
import rootReducer from './reducer';
const Store = configureStore({ reducer: rootReducer })
export default Store;
index.js
enter image description here
input.js
enter image description here
You do not need bindActionCreators when using useDispatch. Simply run dispatch with your action creator function:
export const Add=()=>{
const dispatch = useDispatch();
return(
<>
<button onClick={() => dispatch(AddNumber())}>+</button>
</>
)
}
Reducer->updown.js
File In return object.
So, return {state.value-1}

React Redux not displaying the data when trying to dispatch (Firestore)

I'm trying to use React Redux on my application. I will be building something like instagram, so the scale of the application is going to be big. I have implement redux correctly (I believe) although I'm not getting the fetch result from firestore on the dispatch function, if I insert the same code before the return it works.
action/index.ts:
import { db, auth } from '../../services/firebase';
import { USER_STATE_CHANGE } from '../constants';
export function fetchUser() {
return (dispatch: any) => {
db.collection('users')
.doc(auth.currentUser!.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
console.log(snapshot)
dispatch({
type: USER_STATE_CHANGE,
payload: snapshot.data(),
});
}
});
};
}
constants/index.ts:
export const USER_STATE_CHANGE = 'USER_STATE_CHANGE';
reducers/index.ts:
import { combineReducers } from 'redux';
import { user } from './user';
const Reducers = combineReducers({
userState: user,
});
export default Reducers;
reducers/user.ts:
const initialState = {
currentUser: null,
};
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.currentUser,
};
};
App.tsx:
// Redux
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './src/redux/reducers';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default function App() {
if (!fontsLoaded) return <></>;
return (
<ThemeProvider theme={theme}>
<Provider store={store}>
<Main />
</Provider>
<ToastMessage />
</ThemeProvider>
);
}
Main.tsx:
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { AuthRoutes } from './routes/auth.routes';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchUser } from './redux/actions';
import { auth } from './services/firebase';
function Main() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
fetchUser();
auth.onAuthStateChanged((user) => {
if (user) setIsAuthenticated(true);
});
}, []);
return (
<NavigationContainer>
<AuthRoutes isAuthenticated={isAuthenticated} setIsAuthenticated={setIsAuthenticated} />
<StatusBar style="auto" />
</NavigationContainer>
);
}
const mapDispatchProps = (dispatch: any) => bindActionCreators({ fetchUser }, dispatch);
export default connect(null, mapDispatchProps)(Main);
The fetchUser() is called here, and should log the data from firestore, although I don't get any data logged.
The function is beeing called:
on reducer/user.ts
Have you try to do this:
const initialState = {
currentUser: null,
};
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.payload,
};
};
Replace this
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.currentUser,
};
};
With this
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.payload,
};
};
Because you are setting data in payload but you are accessing from currentUser which empty. Either replace payload with currentUser in useDispatch or replace currentUser with payload in reducer.

useSelector Returning Undefined object

I am trying to store users fetched by random users api and trying to dispatch it to my store i have store users with an empty array, when i am trying to get users using useSelector i am getting an undefined object.
Here is my store.js:
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
import userReducer from "../features/users/userSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
},
});
Here is userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "users",
initialState: {
userArray: [],
},
reducers: {
getUsers: (state, action) => {
state.userArray = action.payload;
}
}
})
export const { getUsers } = userSlice.actions;
export const selectUsers = (state) => state.users.userArray;
export default userSlice.reducer;
Here is App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch } from 'react-redux';
import { getUsers, selectUsers } from './features/users/userSlice';
function App() {
const dispatch = useDispatch();
const users = useSelector(selectUsers);
const fetchUsers = async () => {
fetch("https://gorest.co.in/public/v2/users")
.then((response) => response.json())
.then((data) => {
console.log("data=====", data);
dispatch(getUsers(data));
});
};
return (
<div className="App">
<header className="App-header">
<button onClick={fetchUsers}>Get Users</button>
{users.length > 0 &&
users.map((user) => {
<li>user.name</li>;
})}
</header>
</div>
);
}
export default App;
you are mixing up state.user and state.users
either rename user in configureStore to users or use state.user.userArray

Getting an error that I need to use middleware, but I have already applied middleware

Okay so I am following a tutorial, and I'm a beginner. This is my first experience with Redux.
This is the error I've been getting when it should be displaying the home screen of my webpage.
Actions must be plain objects. Instead, the actual type was: 'string'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.
I have been searching everywhere but it looks to me like I applied thunk correctly. I'm hoping someone more experienced will be able to spot my mistake. Thank you.
HomeScreen.js
import React, { useEffect } from 'react';
import {Link} from 'react-router-dom';
import { listProducts } from '../actions/productActions.js';
import { useDispatch, useSelector } from 'react-redux';
function HomeScreen() {
const productList = useSelector(state => state.productList);
const { products, loading, error} = productList;
const dispatch = useDispatch();
useEffect(() => {
dispatch(listProducts());
return () => {
//
};
}, [])
return loading? <div>Loading...</div> :
error? <div>{error}</div>:
<ul className="products">
{
products.map(product =>
<li key={product._id}>
<div className="product">
<Link to={'/product/' + product._id}>
<img className="product-image" src={product.image} alt="product" />
</Link>
<div className="product-name">
<Link to={'/product/' + product._id}>{product.name}</Link>
</div>
<div className="product-brand">{product.brand}</div>
<div className="product-price">${product.price}</div>
<div className="product-rating">{product.rating} Stars ({product.numReviews} Reviews)</div>
</div>
</li>)
}
</ul>
}
export default HomeScreen;
store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { productListReducer } from './reducers/productReducers.js';
import thunk from 'redux-thunk';
import * as compose from 'lodash.flowright';
const initialState = {};
const reducer = combineReducers({
productList: productListReducer,
})
const composeEnhancer = window.__REDUXDEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
productActions.js
import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from "../constants/productconstants.js";
import axios from "axios";
const listProducts = () => async (dispatch) => {
try {
dispatch(PRODUCT_LIST_REQUEST);
const {data} = await axios.get("/api/products");
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
}
catch (error) {
dispatch({type: PRODUCT_LIST_FAIL, payload:error.message});
}
}
export {listProducts};
productReducers.js
import { PRODUCT_LIST_FAIL, PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS } from "../constants/productconstants";
function productListReducer(state= {products: [] }, action) {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading:true};
case PRODUCT_LIST_SUCCESS:
return {loading:false, products: action.payload};
case PRODUCT_LIST_FAIL:
return {loading:false, error: action.payload};
default:
return state;
}
}
export { productListReducer }
PRODUCT_LIST_REQUEST appears to be a string. You cannot dispatch a string by itself - only action objects. Actions are always objects that have a type field inside, like {type: 'counter/incremented'}.
That said, you should be using our official Redux Toolkit package to write your Redux code. Redux Toolkit will simplify all of the Redux store setup and reducer logic you've shown.

'map' is undefined with Redux and React

Getting a weird error where 'map' is undefined. I'm not sure if my functions are firing at the wrong time and that's resulting in no data being received.
I'm adding Redux into my simple little application that just pulls data from an API and displays it. It's a list of a bunch of Heroes. Like I said before, I think that the error is coming from different times in the ansyc API call and when Redux is firing. But then again I'm a novice so any help is much appreciated.
import React, {useEffect} from 'react'
import { connect } from 'react-redux'
import { fetchHeroes } from '../actions/heroesActions'
import { Hero } from '../components/Hero'
const HeroesPage = ({ dispatch, loading, heroes, hasErrors }) => {
useEffect(() => {
dispatch(fetchHeroes())
}, [dispatch])
const renderHeroes = () => {
if (loading) return <p>Loading posts...</p>
if (hasErrors) return <p>Unable to display posts.</p>
return heroes.map(hero => <Hero key={hero.id} hero={hero} />)
}
return (
<section>
<h1>Heroes</h1>
{renderHeroes()}
</section>
)
}
// Map Redux state to React component props
const mapStateToProps = state => ({
loading: state.heroes.loading,
heroes: state.heroes.heroes,
hasErrors: state.heroes.hasErrors,
})
export default connect(mapStateToProps)(HeroesPage)
export const GET_HEROES = 'GET HEROES'
export const GET_HEROES_SUCCESS = 'GET_HEROES_SUCCESS'
export const GET_HEROES_FAILURE = 'GET_HEROES_FAILURE'
export const getHeroes = () => ({
type: GET_HEROES,
})
export const getHeroesSuccess = heroes => ({
type: GET_HEROES_SUCCESS,
payload: heroes,
})
export const getHeroesFailure = () => ({
type: GET_HEROES_FAILURE,
})
export function fetchHeroes() {
return async dispatch => {
dispatch(getHeroes())
try {
const response = await fetch('https://api.opendota.com/api/heroStats')
console.log(response)
const data = await response.json()
dispatch(getHeroesSuccess(data))
} catch (error) {
dispatch(getHeroesFailure())
}
}
}
index.js where I created the store
// External imports
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
// Local imports
import App from './App'
import rootReducer from './reducers'
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
import React, {useEffect} from 'react'
import { useSelector } from 'react-redux'
import { fetchHeroes } from '../actions/heroesActions'
import { Hero } from '../components/Hero'
const HeroesPage = () => {
const data = useSelector(state => state.heroes);
useEffect(() => {
fetchHeroes();
}, [])
const renderHeroes = () => {
if (data.loading) return <p>Loading posts...</p>
if (data.hasErrors) return <p>Unable to display posts.</p>
return data.heroes.map(hero => <Hero key={hero.id} hero={hero} />)
}
return (
<section>
<h1>Heroes</h1>
{renderHeroes()}
</section>
)
}
export default HeroesPage
action file
// import store from your createStore file and access dispatch from it
dispatch = store.dispatch
export const GET_HEROES = 'GET HEROES'
export const GET_HEROES_SUCCESS = 'GET_HEROES_SUCCESS'
export const GET_HEROES_FAILURE = 'GET_HEROES_FAILURE'
export const getHeroes = () => ({
type: GET_HEROES,
})
export const getHeroesSuccess = heroes => ({
type: GET_HEROES_SUCCESS,
payload: heroes,
})
export const getHeroesFailure = () => ({
type: GET_HEROES_FAILURE,
})
export const fetchHeroes = () => {
dispatch(getHeroes())
try {
const response = await fetch('https://api.opendota.com/api/heroStats')
console.log(response)
const data = await response.json()
dispatch(getHeroesSuccess(data))
} catch (error) {
dispatch(getHeroesFailure())
}
}
reducer file
import * as actions from '../actions/heroesActions'
export const initialState = {
heroes: [],
loading: false,
hasErrors: false,
}
export default function heroesReducer(state = initialState, action) {
switch (action.type) {
case actions.GET_HEROES:
return { ...state, loading: true }
case actions.GET_HEROES_SUCCESS:
return { heroes: action.payload, loading: false, hasErrors: false }
case actions.GET_HEROES_FAILURE:
return { ...state, loading: false, hasErrors: true }
default:
return state
}
}

Categories

Resources