SetAll from CreateEntityAdapter do not set the state with react redux toolkit - javascript

I am trying to use Redux Toolkit in my new website and I have a problem using the createEntityAdapter.
I am fetching some data from my database but my state is never updated with the data fetched and I do not understand why.
My slice and fetch functions:
export const getTransports = createAsyncThunk('marketplace/transports/getTransports', async email => {
const response = await axios.get(`${API_URL}/transport/published/company/${email}/notActive`);
console.log('response', response);
const data = await response.data;
console.log('DATA', data);
return data;
});
const transportsAdapter = createEntityAdapter({});
export const { selectAll: selectTransports, selectById: selectTransportById } = transportsAdapter.getSelectors(
state => state.marketplace.transports
);
const transportsSlice = createSlice({
name: 'marketplace/transports',
initialState: transportsAdapter.getInitialState({
searchText: ''
}),
reducers: {
setTransportsSearchText: {
reducer: (state, action) => {
state.searchText = action.payload;
},
prepare: event => ({ payload: event.target.value || '' })
},
extraReducers: {
[getTransports.fulfilled]: transportsAdapter.setAll
}
}
});
export const { setTransportsSearchText } = transportsSlice.actions;
export default transportsSlice.reducer;
The data fetch is working well and the state between the request and de fullfilled looks like working as it should be, but as you can see in the console, the transports state is never updated.
Redux Logger
I do not understand why is not working the setAll function from the transportsAdapter.
The entities that are being retrieved have and id and the entity information, so it should work correctly but it does not.
I hope you can help me.
Thank you very much.

I was facing the same issue following along the Redux Essentials Tutorial.
I fixed it by specifying the parameters to the function setAll() and using the builder callback approach.
const transportsSlice = createSlice({
name: 'marketplace/transports',
initialState: transportsAdapter.getInitialState({
searchText: ''
}),
reducers: {
setTransportsSearchText: {
reducer: (state, action) => {
state.searchText = action.payload;
},
prepare: event => ({ payload: event.target.value || '' })
},
extraReducers: (builder) => {
builder.addCase(getTransports.fulfilled, (state, action) => {
transportsAdapter.setAll(state, action.payload);
});
},
});

Related

Redux not showing result

i need help with redux toolkit, i'm using redux to fetch one array of objects but this not happen bellow is my redux code and get api.
GET API
export const getProductsApi = async () => {
const response = await fetch(
`${gateway.url}/products`,
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
}
);
const data = await response.json();
return data;
}
MY REDUX ACTION
import {
getProductsPending,
getProductsSuccess,
getProductsFailed,
} from "../../slices/get-products";
import { getProductsApi } from "../../../api/get-products";
export const getProducts = () => async (dispatch: any) => {
dispatch(getProductsPending());
try {
const response = await getProductsApi();
const data = response.data;
console.log('products redux here', data);
dispatch(getProductsSuccess(data));
} catch (error) {
dispatch(getProductsFailed(error));
}
};
REDUX SLICE
const getProductsSlice = createSlice({
name: "getProducts",
initialState,
reducers: {
getProductsPending: (state) => {
state.isLoading = true;
state.error = "";
},
getProductsSuccess: (state, action: PayloadAction<Product[]>) => {
state.isLoading = false;
state.products = action.payload;
state.error = "";
},
getProductsFailed: (state, action) => {
state.isLoading = false;
state.error = action.payload.error;
state.products = [];
},
},
});
const { reducer, actions } = getProductsSlice;
export const { getProductsPending, getProductsSuccess, getProductsFailed } =
actions;
export default reducer;
REDUX STORE
import { configureStore } from "#reduxjs/toolkit";
import getProductsReducer from "../redux/slices/get-products";
const store = configureStore({
reducer: {
products: getProductsReducer,
},
});
store.subscribe(() => console.log(store.getState()));
export default store;
in my browser console when is to appear the data, data appear so
whit my products array empty, and i don't know where is my error because anothers redux i made, i make like this and work correctly.
I appreciate every help to solve my question.
using redux tookit

Can't get the action.payload go to the state Redux Toolkit

So I'm new to React and Redux Toolkit, I've been trying to get some 'posts' from my localhost API, I do get the payload and it gets displayed in the Redux Dev Tools, nevertheless I can't get this payload to be put on the state.
My postSlice.js :
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
export const getPosts = createAsyncThunk("posts", async () => {
try {
const res = await axios({
method: "get",
url: `${process.env.REACT_APP_API_URL}api/post`,
withCredentials: true,
});
console.log(res.data);
return res.data;
} catch (err) {
return err.res.data;
}
});
const postsSlice = createSlice({
name: "posts",
initialState: {
posts: [],
loading: false,
error:"",
},
extrareducers: {
[getPosts.pending]: (state, action) => {
state.loading = true;
},
[getPosts.fulfilled]: (state, action) => {
console.log(action.payload);
state.loading = false;
state.posts = action.payload;
},
[getPosts.rejected]: (state, action) => {
state.loading = false;
state.posts = action.payload.message;
},
},
});
export default postsSlice.reducer;
Then I use the dispatch on a React Component
import { useDispatch } from "react-redux";
import { getPosts } from "../redux/features/postSlice";
const Thread = () => {
const [loadPost, setLoadPost] = useState(true);
const dispatch= useDispatch();
useEffect(() => {
if (loadPost ) {
dispatch(getPosts());
setLoadPost(false)
}
}, [loadPost, dispatch]);
return <div>Fil d'actualités</div>;
};
export default Thread;
Finally I get this on the State in Redux Dev Tool
posts(pin):[]
loading(pin): false
error(pin): ""
And this on the Action:
payload (pin): [{...}{...}{...}]
Also, through the pending, fulfilled and rejected states, loading won't change a bit, even if I pass it on true or false directly with VSCode nor the log I put on the fulfilled extra reducer, it's like the action doesn't affect the state at all, therefore I have the another reducer working fine with the async functions, any help would be much appreciated!
EDIT: My bad, i made a misspelling mistake, 'r' instead of 'R' in 'extraReducers' declaration, the kind of error you feel so dumb about
I think you should use extraRedcuer that way:
extraReducers: (builder) => {
builder.addCase(getPosts.pending, (state, action) => {
state.loading = true;
}),
builder.addCase(getPosts.fulfilled, (state, action) => {
console.log(action.payload);
state.loading = false;
state.posts = action.payload;
}),
builder.addCase(getPosts.rejected, (state, action) => {
state.loading = false;
state.posts = action.payload.message;
}),
},

Migrating from Redux to Redux toolkit

I'm slowly migrating over from Redux to Redux toolkit. I'm still pretty new but I have this login action function. How can I translate old function below do I need createAsyncThunk to achieve this?
export const login = (email, password) => (dispatch) => {
dispatch(requestLogin());
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((user) => {
dispatch(responseLogin(user));
})
.catch((error) => {
dispatch(loginError());
});
};
and my auth slice looks something like this:
const authSlice = createSlice({
name: "authSlice",
initialState: {
isLoggingIn: false,
isLoggingOut: false,
isVerifying: false,
loginError: false,
logoutError: false,
isAuthenticated: false,
user: {},
},
reducers: {
signInWithEmail: (state, action) => {
const { email, password } = action.payload;
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const {
uid,
email,
emailVerified,
phoneNumber,
password,
displayName,
photoURL,
} = response.user;
})
.catch((error) => {
console.log(error);
});
},
},
extraReducers: {},
});
Lets create a productSlice.js
import { createSlice,createSelector,PayloadAction,createAsyncThunk,} from "#reduxjs/toolkit";
export const fetchProducts = createAsyncThunk(
"products/fetchProducts", async (_, thunkAPI) => {
try {
const response = await fetch(`url`); //where you want to fetch data
return await response.json();
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
});
const productsSlice = createSlice({
name: "products",
initialState: {
products: [],
loading: "idle",
error: "",
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchProducts.pending, (state) => {
state. products = [];
state.loading = "loading";
});
builder.addCase(
fetchProducts.fulfilled, (state, { payload }) => {
state. products = payload;
state.loading = "loaded";
});
builder.addCase(
fetchProducts.rejected,(state, action) => {
state.loading = "error";
state.error = action.error.message;
});
}
});
export const selectProducts = createSelector(
(state) => ({
products: state.products,
loading: state.products.loading,
}), (state) => state
);
export default productsSlice;
In your store.js add productsSlice: productsSlice.reducer in out store reducer.
Then for using in component add those code ... I'm also prefer to use hook
import { useSelector, useDispatch } from "react-redux";
import { fetchProducts,selectProducts,} from "path/productSlice.js";
Then Last part calling those method inside your competent like this
const dispatch = useDispatch();
const { products } = useSelector(selectProducts);
React.useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
And Finally, you can access data as products in your component.
The reducer you showed is very wrong. Reducers must never do anything async!
You don't need createAsyncThunk, but if you want to use it, it'd be like this:
export const login = createAsyncThunk(
'login',
({email, password}) => firebase.auth().signInWithEmailAndPassword(email, password)
);
const authSlice = createSlice({
name: "authSlice",
initialState: {
isLoggingIn: false,
isLoggingOut: false,
isVerifying: false,
loginError: false,
logoutError: false,
isAuthenticated: false,
user: {},
},
reducers: {
/* any other state updates here */
},
extraReducers: (builder) => {
builder.addCase(login.pending, (state, action) => {
// mark something as loading here
}
builder.addCase(login.fulfilled, (state, action) => {
// mark request as complete and save results
}
}
});
Note that createAsyncThunk only allows one argument to be passed to the thunk action creator, so it now must be an object with both fields instead of separate arguments.

redux-toolkit action not triggered in reducer

I'm trying to trigger a simple action using #reduxjs/Toolkit but it's not working.
I see that the action is dispatched but it's like the slice reducer is not listening to it or something.
const say = createAction("ui/say", what => ({ payload: what }));
const uiSlice = createSlice({
name: "ui",
initialState: { said: "" },
reducers: {
[say.type]: (state, action) => {
console.log("saying", action.payload); //<-- not showing, why?
state.currentList = action.payload;
}
}
});
const store = configureStore({
reducer: combineReducers({
ui: uiSlice.reducer
})
});
const Chat = () => {
const dispatch = useDispatch();
const [whatToSay, setWhatToSay] = useState("");
const whatWasSaid = useSelector(state => state.ui.said);
const onSubmit = e => {
e.preventDefault();
dispatch(say(whatToSay));
setWhatToSay("");
};
return (
<div>
<form onSubmit={onSubmit}>
<input type="text" onChange={e => setWhatToSay(e.target.value)} />
<button>Say</button>
</form>
{whatWasSaid ? <p>You said: {whatWasSaid}</p> : <p>Say something</p>}
</div>
);
};
Here's a minimal reproducing example:
https://codesandbox.io/s/redux-toolkit-0tzxs?file=/src/index.js
I think you mismatched the createSlice API.
From your code, you trying to implement a listener for an action, so you might want to use extraReducers instead:
const uiSlice = createSlice({
name: "ui",
initialState: { said: "" },
// Not reducers: {}
extraReducers: {
[say.type]: (state, action) => {
console.log("saying", action.payload);
state.currentList = action.payload;
}
}
});
Note the reducers prop of createSlice API:
reducers: Object<string, ReducerFunction | ReducerAndPrepareObject>
If you want to use say in reducers it should be:
const say = (state, payload) => {
console.log("saying", payload);
state.currentList = payload;
};
const uiSlice = createSlice({
name: "ui",
initialState: { said: "" },
reducers: { say }
});
// Usage
dispatch(uiSlice.actions.say(whatToSay));
#markerikson: with createSlice, the reducers field is for defining reducers and generating actions that will match those reducers. The extraReducers field is for handling actions that were already defined elsewhere.

Access the state of my redux app using redux hooks

I am migrating my component from a class component to a functional component using hooks. I need to access the states with useSelector by triggering an action when the state mounts. Below is what I have thus far. What am I doing wrong? Also when I log users to the console I get the whole initial state ie { isUpdated: false, users: {}}; instead of just users
reducers.js
const initialState = {
isUpdated: false,
users: {},
};
const generateUsersObject = array => array.reduce((obj, item) => {
const { id } = item;
obj[id] = item;
return obj;
}, {});
export default (state = { ...initialState }, action) => {
switch (action.type) {
case UPDATE_USERS_LIST: {
return {
...state,
users: generateUsersObject(dataSource),
};
}
//...
default:
return state;
}
};
action.js
export const updateUsersList = () => ({
type: UPDATE_USERS_LIST,
});
the component hooks I am using
const users = useSelector(state => state.users);
const isUpdated = useSelector(state => state.isUpdated);
const dispatch = useDispatch();
useEffect(() => {
const { updateUsersList } = actions;
dispatch(updateUsersList());
}, []);
first, it will be easier to help if the index/store etc will be copied as well. (did u used thunk?)
second, your action miss "dispatch" magic word -
export const updateUsersList = () =>
return (dispatch, getState) => dispatch({
type: UPDATE_USERS_LIST
});
it is highly suggested to wrap this code with { try } syntax and be able to catch an error if happened
third, and it might help with the console.log(users) error -
there is no need in { ... } at the reducer,
state = intialState
should be enough. this line it is just for the first run of the store.
and I don't understand where { dataSource } comes from.

Categories

Resources