Why is my dispatch not performing the console.log? - reactjs

SO im having a hard time understanding reducers and dispatch, and useSelectors.
In this example, im clicking a button via onRowItemClick. This should in theory, dispatch an action, which is playAudio.
That action is inside a reducer? And i would imagine it should console.log an empty array, which is the current state. However, when i click it, nothing happens in the console.
Slice:
import { createSlice } from '#reduxjs/toolkit';
const initialState = [];
const AudioSlice = createSlice({
name: 'AudioSlice',
initialState,
reducers: {
playAudio(state){
console.log(current(state));
}
},
});
export const { playAudio} = AudioSlice.actions;
export default AudioSlice.reducer;
The dispatch:
import React from 'react';
import { useDispatch } from 'react-redux';
import { playAudio } from 'slices/AudioSlice';
const ProjectPageAudioTab = () => {
const dispatch = useDispatch();
const onRowItemClick = () => {
dispatch(playAudio());
};
}
export default ProjectPageAudioTab;

Related

Why my dispatch won't change the state? "Redux Toolkit"

I'm practicing Redux and in my project when i dispatch an action i will see that in the dev tools but the state won't change and it will remain an empty Array. why is this happening ?
i will appreciate a little help
shopping cart.js
import { createSlice, createAction } from "#reduxjs/toolkit";
// Action Creator
export const itemAdd = createAction("products/itemAdded");
const slice = createSlice({
name: "shoppingCart",
initialState: [],
reducers: {
itemAdded: (cart, action) => cart.push(action.payload),
},
});
export const { itemAdded} = slice.actions;
export default slice.reducer;
Dispatch
import { useDispatch, useSelector } from "react-redux";
import { itemAdd } from "../../store/state/shoppingCart";
// It's an onClick Event Handler
const handleAddItem = (item) => {
dispatch(itemAdd(item));
};
Also, your reducer needs braces.
itemAdded: (cart, action) => { cart.push(action.payload) }
Otherwise it will return the return value of cart.push, which is the new length of the array - and immer can't do its work if you modify the state and return something unrelated at the same time.
Because you are importing itemAdd
import { itemAdd } from "../../store/state/shoppingCart";
instead of itemAdded
import { itemAdded } from "../../store/state/shoppingCart";

Calling API in redux

//Store
import { configureStore } from "#reduxjs/toolkit";
import { currencyListSlice } from "./Reducers/CurrencyListReducer";
export const store = configureStore({
reducer: {
currencyList: currencyListSlice.reducer,
}
}
)
export default store
//CurrencyListReducer
import { createSlice } from "#reduxjs/toolkit"
export const loadCurrencyList = () => {
return async (dispatch, getState) => {
const data = await fetch(API-Key)
const payload = await data.json()
dispatch({
type: 'currencyList/setCurrencyList',
payload: payload
})
}
}
const options = {
name: 'currencyList',
initialState: [],
reducers: {
setCurrencyList(state, action) {
return action.payload
}
}
}
export const currencyListSlice = createSlice(options)
//CurrencyList Component
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { currencyListSlice } from '../../Reducers/CurrencyListReducer'
const selectCurrencyList = state => state.CurrencyList
export const CurrencyList = () => {
const dispatch = useDispatch()
const currencyList = useSelector(selectCurrencyList)
const { loadCurrencyList } = currencyListSlice.actions
useEffect(() => {
dispatch(loadCurrencyList())
}, [dispatch, loadCurrencyList])
console.log(currencyList)
return (
<div>
/*Some elements here*/
</div>
)
}
I'm working with redux for the first time and having some real problem in calling API and storing data in store. The problem is I'm not getting anything from API but the console.log(currencyList) just gives me undefined. I tried calling API directly in reducer but that too didn't work out. I'm a newbie to redux and calling the API in redux is being a difficult task for me. Forgive any silly mistake(if present).
try reading this: createAsyncThunk

reducer/action not updating slice

I am clearly missing something about how this should work. I have a slice, it has reducers I can bring those in and I can see console.log firing as expected... buuut Redux Dev Tools says I have not changed my state - the default null still is still the listed value.
Slice
import { User } from "#firebase/auth";
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
//Slice definition
// define state
interface UserState {
currentUser: User | null;
}
const initialState: UserState = {
currentUser: null,
};
//define action creators
const userSlice = createSlice({
name: "user",
initialState,
reducers: {
setUser(state, action: PayloadAction<User>) {
state.currentUser = action.payload;
},
setNoUser(state) {
state.currentUser = null;
},
},
});
//export all action creators
export const { setUser, setNoUser } = userSlice.actions;
//export reducer that handles all actions
export default userSlice.reducer;
Store
import { configureStore } from "#reduxjs/toolkit";
import userReducer from "../features/user/user-slice";
export const store = configureStore({
reducer: { user: userReducer },
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
App snippet
import React, { useEffect, useState } from "react";
...
import { setUser, setNoUser } from "./features/user/user-slice";
function App() {
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
auth.onAuthStateChanged((user) => {
if (user) {
console.log("useEffect");
console.log(user);
setUser(user);
} else {
setNoUser();
console.log("useEffect, no user");
}
setLoading(false);
});
});
if (loading) return <Spinner animation="border" color="dark" />;
return <App/>;
}
export default App;
the reason for this is because setUser is an action and the action needs to be dispatched.
Per Mark Erikson's Video from may it is advisable to create a typed version of the useSelector and useDispatch hooks when working with typescript.
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const userAppSelector: TypedUseSelectorHook<RootState> = useSelector;
you then import the hooks from the hooks file not react-redux and dispatch or select from there:
const dispatch = useAppDispatch();
dispatch(setUser(auth.currentUser));

How can I access my dispatch routines from my props using TypeScript + Redux?

Take a look at the following slice I've created in TypeScript (storeSlice.ts):
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
import axios from "axios";
import { AppThunk } from "../../app/store";
import { Summoner } from "../../models/Summoner";
export interface StoreState {
summoners: Summoner[]
}
const initialState: StoreState = {
summoners: []
}
export const storeSlice = createSlice({
name: 'store',
initialState,
reducers: {
getSummonersSuccess: (state: StoreState, action: PayloadAction<Summoner[]>) => {
state.summoners = action.payload;
}
}
});
export const { getSummonersSuccess } = storeSlice.actions;
export const getSummoners = (): AppThunk => (dispatch) => {
axios.get("api/summoners").then((response) => {
dispatch(getSummonersSuccess(response.data));
});
}
export default storeSlice.reducer;
It's pretty basic. It has an initial state that is of type StoreState which contains an array of type Summoner.
Now, the component that uses this slice looks like this (Store.tsx):
import React, { Dispatch } from 'react';
import { connect } from 'react-redux';
import { getSummoners, StoreState } from './storeSlice';
interface PropState {
store: StoreState
}
const Store = (props: StoreState) => {
console.log("store props", props); //This contains my "getSummoners" action, but I can't access it because it's not defined in "StoreState"
return (
<h1>Hello!</h1>
);
}
const mapStateToProps = (state: PropState) => {
console.log("Store state", state);
return { summoners: state.store.summoners };
};
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
return {
getSummoners: dispatch(getSummoners)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Store);
When I log the props in the component, I get the state that I've mapped with mapStateToProps, and I also get the getSummoners action that I've mapped with mapDispatchToProps.
However, I cannot actually access the getSummoners action because it is not defined in StoreState.
I do not want to have to define the actions in every default state type that I create.
Is there something I can do to be able to use the dispatch actions in my code whilst keeping TypeScript happy?
Apologies if this question doesn't make too much sense, I'm fairly new to this tech stack.
In your mapDispatchToProps you are inadvertanly calling the dispatch rather than returning a function to call the dispatch from your component. Change it to:
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
return {
getSummoners: () => dispatch(getSummoners)
}
}
This should clear up your problems. If it doesn't, please update your post with the exact error that you are getting.
As a sidenote, this connect higher-order component with mapStateToProps and mapDispatchToProps has been around for a while and it used to be the only way to get data from the state into your component. It still works, but nowadays it is recommended that you use the newer hooks useSelector and useDispatch instead.

using hooks with react redux. Reducer not getting hit. No state

I am working on a react application using redux with hooks.
Here is my action creator below
PostAction
***********
import * as types from "./actionTypes";
import axios from 'axios';
const ROOT_URL = 'http://dotsuper.com/api'
export function fetchPosts(){
const request = axios.get(`${ROOT_URL}/post/getposts`)
return {
type: types.GETALL_POSTS,
payload: request
}
}
Here is my reducer below
PostReducer
************
import _ from 'lodash';
import * as types from "../actions/actionTypes";
export default function postReducer(state = [], action) {
switch (action.type) {
case types.GETALL_POSTS:
debugger;
console.log(action.payload.data);
return _.mapKeys(action.payload.data, 'id');
default:
return state;
}
}
Here is what my store configuration looks like
configureStore
***************
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "./reducers";
import reduxImmutableStateInvariant from "redux-immutable-state-invariant";
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; //add support for redux dev tools.
return createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(thunk, reduxImmutableStateInvariant()))
);
}
Here is what my component looks like.
My question is when I look at my devtools, the state for posts is
completely empty. When I set a debugger, my PostReducer is not getting hit. I think I am still
missing something. I don't think you can use connect with hooks. What do I need to do below
to have data in my state and be able to hit my post reducer?
PostPage
**************
import React, { useState, useEffect } from "react";
import {fetchPosts} from "../../redux/actions/postActions";
const PostsPage = () => {
const [getPosts, setGetPosts] = useState([]);
async function fecthData(){
const res = fetchPosts()
}
useEffect( () => {
fecthData();
},[]);
return (
<div>
<h2>Posts</h2>
<p>
This page is for all the posts.
</p>
</div>
);
}
export default PostsPage;
You're calling the action generator fetchPosts() inside your component, but you actually never dispatch any change into your state. If you look closely you'll see that you're fetchPosts() returns an object commonly known as actions:
{
type: types.GETALL_POSTS,
payload: request
}
So basically when you call the fetchPosts, you fetch something and you return this object. No touching to the Redux state so far
In the next step you should actually take this object and dispatch it to your store, like this:
const action = await fetchPosts();
dispatch(action);
Which when you use connect with mapDispatchToProps the connect will take care of it for you.
Check here to get a better grasp of the concept.
When using with hooks however, you can import these from react-redux:
useDispatch instead of mapDispatchToProps and,
useSelector instead of mapStateToProps
import {useDispatch, useSelector} from 'react-redux';
import myAction from 'path/to/my/action';
const MyComponent = (props) => {
const myState = useSelector(state => state.myState);
const dispatch = useDispatch();
const handleClick = () => {
dispatch(myAction());
}
return (
...
)
}
Check inside fetchPosts method, axios.get returns promise. you need to make
the method async and handle async data.
You need to dispatch the action in order to bind the action with the redux state.
// PostAction
import * as types from "./actionTypes";
import axios from 'axios';
const ROOT_URL = 'http://dotsuper.com/api'
export function fetchPosts(){
return async (dispatch) => {
const request = await axios.get(`${ROOT_URL}/post/getposts`); // returns promise.
dispatch({
type: types.GETALL_POSTS,
payload: request
});
}
}
// PostPage
import React, { useState, useEffect } from "react";
import {useDispatch} from "react-redux";
import {fetchPosts} from "../../redux/actions/postActions";
const PostsPage = () => {
const [getPosts, setGetPosts] = useState([]);
const dispatch = useDispatch();
useEffect( () => {
dispatch(fetchPosts());
},[]);
return (
<div>
<h2>Posts</h2>
<p>
This page is for all the posts.
</p>
</div>
);
}
export default PostsPage;

Resources