I am trying to store data in my redux store using a reducer when we get data from B/E API and trying to store data in the reducer(store). But not working. My code is correct to me. But somehow it's not working.
My Store:-
import createSagaMiddleware from 'redux-saga';
import { configureStore } from '#reduxjs/toolkit';
import rootReducer from './rootReducer';
import rootSaga from './rootsaga';
// saga middleware
const sagaMiddleware = createSagaMiddleware();
// redux store
const store = configureStore({
reducer: rootReducer,
middleware: [sagaMiddleware],
});
// run saga middleware
sagaMiddleware.run(rootSaga);
export default store;
My root saga:-
import { all, takeEvery } from '#redux-saga/core/effects';
import * as categoryActionTypes from '../redux/types/categoryActionTypes';
import * as productActionTypes from '../redux/types/productActionTypes';
import { getAllStudents } from './sagas/categorySaga';
import { getAllProductWatcher } from './sagas/productSaga';
export default function* root() {
yield all([
takeEvery(categoryActionTypes.GET_ALL_CATEGORY, getAllStudents),
takeEvery(productActionTypes.GET_ALL_PRODUCT_ACTION, getAllProductWatcher),
]);
}
My Reducer:- (console.log() not working) That's means not store data into reducer
import * as actionTypes from '../types/productActionTypes';
const initialState = {
product: [],
isLoading: true,
error: true,
};
// reducer
export default function productReducer(state = initialState, action) {
switch (action.types) {
case actionTypes.GET_ALL_PRODUCT_SUCCESS:
console.log(action.data);
return {
...state,
product: action.data,
isLoading: false,
error: false,
};
default:
return state;
}
}
This is my saga method and this is how I store data into my reducer:-
import { put } from '#redux-saga/core/effects';
import { GET_ALL_PRODUCT_SUCCESS } from '../types/productActionTypes';
import { createRequest } from '#utils/create-request';
export function* getAllProductWatcher(upload) {
try {
const Axios = yield createRequest(upload);
const res = yield Axios.get('http://localhost:8080/product');
if (res) {
**//Tying to store API data to my reducer**
yield put({
type: GET_ALL_PRODUCT_SUCCESS,
data: res.data,
});
}
} catch (e) {
console.log(e);
}
}
I really need your help... Thanks.
Related
I am currently working with the redux toolkit I am facing an issue for the last 2 days.
The problem is when I am trying to select a state using the useSelector undefined is returned.
The code is given below:
reducer
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const SchoolDataReducer = createSlice({
name: "datasets",
initialState: [],
reducers: {
getSchoolRecords(state, action) {
return action.payload
}
}
})
export const { getSchoolRecords } = SchoolDataReducer.actions;
export default SchoolDataReducer.reducer;
export function getDataTriger() {
return async function getSchoolDataThunk(dispatch, getstate) {
try {
const res = await axios.get("/getschool")
dispatch(getSchoolRecords(res.data))
} catch (error) {
console.log(error);
}
}
}
store
import { configureStore } from "#reduxjs/toolkit";
import SchoolDataReducer from "./SchoolData"
const store = configureStore({
reducer: {
School: SchoolDataReducer
}
});
export default store;
result
const maindATA = useSelector(State => State.datasets)
console.log(maindATA); // undefined
Your whole state data structure is { School: [] }, you can get the state slice using useSelector(state => state.School).
Please see https://redux-toolkit.js.org/api/configureStore#reducer about the data structure of the redux state. So you can change the reducer option of configureStore like below:
const store = configureStore({
reducer: {
datasets: SchoolDataReducer
}
});
// In component:
const maindATA = useSelector(state => state.datasets)
In my React application, I use saga middleware. This is my root saga.
import { all } from 'redux-saga/effects';
import alertSaga from 'redux/alert/alert.saga';
export default function* rootSaga() {
console.log('ROOT SAGA')
yield all([
alertSaga(),
]);
}
This is the store.
import { applyMiddleware, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { persistStore } from 'redux-persist';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './root-reducer';
import rootSaga from './root-saga';
const INITIAL_STATE = {};
const sagaMiddleware = createSagaMiddleware();
const middleware = [sagaMiddleware];
const store = createStore(
rootReducer,
INITIAL_STATE,
composeWithDevTools(applyMiddleware(...middleware)),
);
sagaMiddleware.run(rootSaga);
const persistor = persistStore(store);
export { store, persistor };
This is my alertSaga.
import { put, takeEvery } from 'redux-saga/effects';
import { IAction } from 'constants/types.constant';
import { removeAlert, setAlert } from 'redux/alert/alert.actions';
import { SET_ALERT } from 'redux/alert/alert.types';
export function* displayAlert({ type, payload }: IAction) {
console.log(type);
console.log(payload);
// yield put(setAlert({ type, payload }));
try {
yield put({ type: SET_ALERT, payload });
} catch (error) {
}
// setTimeout(() => yield put(removeAlert(payload.id), 3000));
}
function* alertSaga() {
console.log('HELLO');
yield takeEvery(SET_ALERT, displayAlert);
}
export default alertSaga;
Finally, this is my alert action.
import { REMOVE_ALERT, SET_ALERT } from './alert.types';
export const setAlert = (payload: Object) => ({ type: SET_ALERT, payload });
export const removeAlert = (payload: number) => ({ type: REMOVE_ALERT, payload });
When the setAlert is dispatched, it doesn't hit to the alert saga. What am I doing wrong here?
if you'd like to export just 1 saga, try below
export default function* alertSaga() {
console.log('HELLO');
yield takeEvery(SET_ALERT, displayAlert);
}
or if you have multiple sagas to export
export default function* yourSagas() {
yield all([
alertSaga(),
otherSaga(),
]);
}
Can't stop infinity loop client error like: https://imgur.com/34BSVu0
This error just typo.
But saga action is repeated endless times.
When I tried without saga, I got only one time error. (Not endless)
I made sandbox: https://codesandbox.io/s/blissful-pine-qvcyv?from-embed
saga:
import { call, put, takeLatest } from 'redux-saga/effects';
function* fetchSome(action: Action) {
try {
const { id } = action.payload;
const { data } = yield call(api, { id });
yield put(fetchSomeSucess({ data }));
} catch (e) {
yield put(fetchSomeFailure(e));
}
}
export function* someProcess() {
yield takeLatest('FETCH_SOME', fetchSome);
}
store:
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { rootReducer } from '../reducers';
import { rootSaga } from '../sagas';
const sagaMiddleware = createSagaMiddleware();
export const runSaga = async () => {
return sagaMiddleware.run(rootSaga);
};
export const configureStore = (initialState: Object = {}) => {
const store = createStore(
rootReducer,
initialState,
applyMiddleware(sagaMiddleware),
);
runSaga();
return store;
};
rootSaga:
import { all, fork } from 'redux-saga/effects';
export function* rootSaga() {
yield all([
fork(someProcess),
]);
}
Make sure your action creators are using the right constants. In this file:
function* fetchSome(action: Action) {
try {
const { id } = action.payload;
const { data } = yield call(api, { id });
yield put(fetchSomeSucess({ data }));
} catch (e) {
yield put(fetchSomeFailure(e));
}
}
You need to make sure that the fetchSomeSucess() and fetchSomeFailure() action creators are not creating an action with type: FETCH_SOME which could easily be a typo.
If either of those have type: FETCH_SOME it would continually call your saga creating an endless loop
I've created a react app using create-react-app and react-redux. I dispatch an action using mapDispatchToProps on clicking a button and it returns a payload. But when I try to retrieve the props using mapStateToProps in my component, it returns the initial state.
What am I doing wrong?
I have tried to debug thoroughly and I realize that the action is dispatched and the payload makes it to the action creator. But the reducer isn't triggered after the action is dispatched.
It might be how I am calling it or how I have set up my reducer as well.
index.js file:
import React from 'react';
import './css/index.css';
import App from './App/App';
import * as serviceWorker from './serviceWorker';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(thunk));
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
reducers/Reducer.js
reducers/Reducer.js
import { GET_RESP } from '../modules/actions'
function getInitialState () {
return {
predictions: [],
score: null
}
}
function gesResp (state, payload) {
return {
...state,
predictions: payload.predictions,
score: payload.score
}
}
export function Reducer (state, action) {
if (!state) {
return getInitialState();
}
switch(action.type) {
case GET_RESP:
return gesResp(state, action.payload);
default:
return state;
}
}
export default Reducer;
reducers/index.js
import { combineReducers } from 'redux';
import Reducer from './Reducer'
const rootReducer = combineReducers({
Reducer
});
export default rootReducer;
action.js
import axios from 'axios';
// actions:
export const GET_RESP = 'GET_RESP';
// action creators:
export function gesResp (payload) {
return {
type: GET_RESP,
payload: payload
}
}
export function fetchRecommendations (description, resp) {
let url = 'myendpointurl';
let requestPayload = {
description: description,
resp: resp
}
return (dispatch) => {
return axios.post(url, requestPayload)
.then(function(res) {
gesResp(res.data);
})
}
}
component file: (I'm only posting related code):
handleSubmit () {
this.props.fetchMyRecommendations(Desc,
this.state.htmlContent);
}
const mapStateToProps = state => {
return {
predictions: state.Reducer.predictions,
score: state.Reducer.score
};
}
const mapDispatchToProps = dispatch => {
return {
fetchMyRecommendations: (Desc, userScore) =>
dispatch(fetchRecommendations(Desc, userScore))
};
}
export default connect(mapStateToProps, mapDispatchToProps)
(HomePage);
Ideally what I want is in the mapStateToProps to return the predictions array and the resp score.
I can see that they are being returned in the network call and showing up the actions call as well. Thanks in advance to whoever can help! :)
You need to dispatch getReccommendations to actually trigger the reducer for your asynchronous action. Try the following:
export function fetchRecommendations (job_description, resume) {
let url = 'myendpointurl';
let requestPayload = {
job_description: job_description,
resume: resume
};
return (dispatch) => {
return axios.post(url, requestPayload)
.then(function(res) {
dispatch(getReccommendations(res.data));
});
}
}
Hopefully that helps!
I'm just starting with React and Redux and stumbled upon something I can't figure out by myself - I think that Redux state is not changing and it's causing (some of) errors. I'm checking state with use of remote-redux-devtools#0.5.0.
My code:
Categories.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { getCategories } from '../../actions/categories';
export class Categories extends Component {
static propTypes = {
categories: PropTypes.array.isRequired
};
componentDidMount() {
this.props.getCategories();
}
render() {
return (
<div>
Placeholder for categories.
</div>
)
}
}
const mapStateToProps = state => ({
categories: state.categories.categories
});
export default connect(mapStateToProps, { getCategories })(Categories);
../../actions/categories.js:
import axios from "axios";
import { CATEGORIES_GET } from "./types";
export const getCategories = () => dispatch => {
return axios
.get("/api/notes/categories/")
.then(res => {
dispatch({
type: CATEGORIES_GET,
payload: res.data
});
})
.catch(err => console.log(err));
};
reducers/categories.js:
import { CATEGORIES_GET } from '../actions/types.js';
const initialState = {
categories: []
};
export default function (state = initialState, action) {
switch (action.type) {
case CATEGORIES_GET:
return {
...state,
categories: action.payload
};
default:
return state;
}
}
store.js:
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'remote-redux-devtools';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware)));
export default store;
reducers/index.js
import { combineReducers } from "redux";
import categories from './categories';
export default combineReducers({
categories,
});
Using remote-redux-devtools, I've never seen anything in my state. Currently this code above gives me 3 errors, two of them being
this.props.getCategories is not a function
My guess is that because there is some issue with Categories class, it's not passing anything to state and it could be root cause of errors. I had one more error, connected to Categories not being called with attributes, but for debug purposes I put empty array there - one error dissapeared, but that's it. I've also tried adding constructor to Categories and called super(), but did not help also.
I believe your issue is that you're exporting your Categories class twice, once connected, the other not.
If you remove export from export class Categories extends Component, does it work as expected?
When you're mapping the state in a component, you must access the desired variable through a reducer.
So instead of:
const mapStateToProps = state => ({
categories: state.categories
});
You must use:
const mapStateToProps = state => ({
categories: state.categories.categories
});
Your props don't have getCategories method, because you didn't pass it as a function to connect.
A better approach is to define only the action code in your actions file and then use mapDispatchToProps.
../../actions/categories.js
import axios from "axios";
export const getCategories = () => {
axios
.get("/api/notes/categories/")
.then(res => res.data);
})
.catch(err => console.log(err));
};
Categories.js
import { getCategories } from '../../actions/categories'
import { CATEGORIES_GET } from "./types";
const mapDispatchToProps = dispatch => {
return {
getCategories: () => dispatch(() => { type: CATEGORIES_GET, payload: getCategories() }),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Categories);