Redux-saga is not updating store states - reactjs

I'm using redux-saga to fetch an endpoint and want to present it on first page load using useEffect(). But mine is not fetching anything. The screen is blank and reduxDevTools is also not showing anything. I can't understand what did I miss.
My saga:
export function* watcherSaga() {
yield takeLatest("FETCH_TOP_NEWS_REQUEST", workerSaga);}
function fetchTopNews() {
return axios({
method: 'get',
url: 'https://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY'
});}
function* workerSaga() {
try{
const response = yield call(fetchTopNews);
const news = response.data.articles;
yield put({ type: "FETCH_TOP_NEWS_SUCCESS", news });
}
catch (error) {
yield put({ type: "FETCH_TOP_NEWS_ERROR", error });
}
}
I defined 3 actions:
const initialState = {
fetching: false,
error: null,
news: []
};
const NewsReducer = (state=initialState, action) => {
switch(action.type){
case types.fetchTopNewsRequest:
return { ...state, fetching: true, error: null };
case types.fetchTopNewsSuccess:
return { ...state, fetching: false, news: action.news[0] };
case types.fetchTopNewsError:
return { ...state, fetching: false, news: null, error: action.error };
default:
return state;
}
}
export default NewsReducer;
At last my component, I imported the fetchTopNewsRequest() action here:
const TopHeadline = (props) => {
const { news, getTopNews } = props;
useEffect(() => {
getTopNews();
}, [getTopNews]);
return (
<div className="newsItem">
<h1>Title: {news.title}</h1>
</div>
);}
const mapStateToProps= (state) => {
return {
news: state.news,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getTopNews: () => dispatch( fetchTopNewsRequest() )
};
};
export default connect(mapStateToProps, mapDispatchToProps)(TopHeadline);
I'm trying to fetch only the articles.title.
DevTools shows it's successfully fetching the data:
Buy my states are not updating:

Related

Component not re-rendering after Redux-Saga call

My component shows incorrect value from store.
The request was successful and after request I've got fetching: false and contacts list in Redux state. But component doesn't update, console.log(fetching) shows true and UI shows "Loading".
In Redux dev tools there's only 'GET_CONTACTS_SUCCESS' action.
Why component doesn't update, where can be the problem? Seems like I do everything as in this answer https://stackoverflow.com/a/64614396/12994741
Component:
const { contacts, fetching } = useSelector(state => state.contact);
useEffect(() => {
dispatch({ type: 'GET_CONTACTS_REQUEST' });
}, [dispatch])
console.log(fetching);
return <div>
{fetching
? <p> Loading </p>
: (contacts.map(contact => <div key={contact.id}>{contact.id}</div>)}
</div>
Saga:
function* getContactsSaga() {
try {
const response = yield call('...');
if (response && response.data) {
yield put({
type: 'GET_CONTACTS_SUCCESS',
items: response.data,
})
} else {
yield put({
type: 'GET_CONTACTS_FAILED',
message: 'Data Access Error. Please Try again later',
});
}
} catch (e) {
yield put({
type: 'GET_CONTACTS_FAILED',
message: e.response.statusText,
});
}
}
function* contactSaga() {
yield takeLatest('GET_CONTACTS_REQUEST', getContactsSaga);
}
Reducer:
const initialState = {
error: '',
fetching: false,
contacts: [],
};
const ContactReducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_CONTACTS_REQUEST':
return {
...state,
fetching: true,
};
case 'GET_CONTACTS_SUCCESS':
return {
...state,
error: '',
fetching: false,
contacts: action.items,
};
case 'GET_CONTACTS_FAILED':
return {
...state,
fetching: false,
error: action.message,
};
default:
return state;
}
}

NextJS redux-saga State not updating

I'm trying to get data in nextjs with redux-saga. But my data wont show, then i debug in every file, and finally found that the data is successfully fetched but, the state wont updated. I've been follow the docs and dont know which part that caused the issue. Thanks for any help
Default State
{
status: 'init',
data: [],
error: null,
}
Saga
export function* getCars() {
try {
const options = {
url: `http://localhost/some-endpoint`,
method: 'GET',
}
yield put({
type: ACTION_TYPES.CARS.LOAD,
});
const response = call(axios, options);
if (response.data) {
const { data, status } = response;
yield put({
type: ACTION_TYPES.CARS.RES,
payload: { data, status },
});
} else {
yield put({
type: ACTION_TYPES.CARS.ERR,
error: response.status,
});
}
} catch (err) {
yield put({
type: ACTION_TYPES.CARS.ERR,
error: err,
});
}
}
export default function* combineSaga() {
yield takeEvery(ACTION_TYPES.CARS.GET, getCars);
}
Reducer
const defaultState = DEFAULT_STATE.CARS;
function reducer(state = defaultState, action) {
switch (action.type) {
case ACTION_TYPES.CARS.LOAD:
return {
...state,
status: 'loading',
};
case ACTION_TYPES.CARS.RES:
return {
...state,
data: action.payload.data,
status: 'success',
};
case ACTION_TYPES.CARS.ERR:
return {
...state,
status: 'error',
error: action.error,
};
default:
return state;
}
}
export default reducer;
Store Configuration
const makeStore = () => {
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware),
)
store.sagaTask = sagaMiddleware.run(rootSaga)
return store
}
export default createWrapper(makeStore);
App.js
function MyApp({ Component, pageProps }) {
return (
<div>
<Component {...pageProps} />
</div>
);
}
MyApp.getInitialProps = async appContext => {
const { Component, ctx } = appContext;
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
export default wrapper.withRedux(withReduxSaga(MyApp));
Index.js
function Home({ storeCars }) {
useEffect(() => {
//always show default state
console.log(storeCars);
}, [storeCars.status]);
return <div>Home</div>;
}
Home.getInitialProps = async props => {
const { store } = props;
store.dispatch({
type: ACTION_TYPES.CARS.GET,
payload: {},
});
return {};
}
export default connect(state => state)(Home);
you need 2 actions
getDataFromServer to be used in Home component
setDataToRedux to be used in saga and action type in reducer

isAuthenticated is undefined react js?

isAuthenticated is undefined when i run this code. how can is use isAuthenticated with mapStateProps. if i am use Token `(Token '5302f4340a76cd80a855286c6d9e0e48d2f519cb'} like this then it's working fine but i want Authorized it with props.isAuthenticated anybody know how can i solve this issue?
authAction.js
import axios from 'axios';
import * as actionTypes from './actionTypes';
export const authStart = () => {
return {
type: actionTypes.AUTH_START
}
}
export const authSuccess = token => {
return {
type: actionTypes.AUTH_SUCCESS,
token: token
}
}
export const authFail = error => {
return {
type: actionTypes.AUTH_FAIL,
error: error
}
}
export const logout = () => {
localStorage.removeItem('token');
return {
type: actionTypes.AUTH_LOGOUT
};
}
export const authLogin = (userData) => {
return dispatch => {
dispatch(authStart());
axios.post('http://localhost:8000/rest-auth/login/', userData)
.then(res => {
const token = res.data.key;
localStorage.setItem('token', token);
dispatch(authSuccess(token));
})
.catch(err => {
dispatch(authFail(err))
})
}
}
authReducer.js
import * as actionTypes from '../actions/actionTypes';
import { updateObject } from '../utility';
const initialState = {
isAuthenticated: null,
token: null,
error: null,
loading: false
}
const authStart = (state, action) => {
return updateObject(state, {
isAuthenticated: false,
error: null,
loading: true
});
}
const authSuccess = (state, action) => {
return updateObject(state, {
isAuthenticated: true,
token: action.token,
error: null,
loading: false
});
}
const authFail = (state, action) => {
return updateObject(state, {
error: action.error,
loading: false
});
}
const authLogout = (state, action) => {
return updateObject(state, {
token: null
});
}
export default function (state = initialState, action) {
switch (action.type) {
case actionTypes.AUTH_START: return authStart(state, action);
case actionTypes.AUTH_SUCCESS: return authSuccess(state, action);
case actionTypes.AUTH_FAIL: return authFail(state, action);
case actionTypes.AUTH_LOGOUT: return authLogout(state, action);
default:
return state;
}
}
index.js
import { combineReducers } from 'redux';
import auth from './authReducer'
export default combineReducers({
auth: auth
});
articleList.js
const NewsList = (props) => {
// ...
const fetchItems = async () => {
try {
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Token ${props.isAuthenticated}`
}
}
const res = await axios.get(`${process.env.REACT_APP_API_URL}/api/`, config);
setItems(res.data)
setLoading(false);
}
catch (err) {
console.log(`😱 Axios request failed: ${err}`);
}
}
fetchItems()
})
}, [items]);
// ...
}
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.token
}
}
export default connect(mapStateToProps)(NewsList)
You need to debug your code. Start by connecting the dots: The output tells you that props.isAuthenticated is undefined. You pass this in from state.auth.token in mapStateToProps():
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.token
}
}
So state.auth.token must be undefined also. That's as far as I can get from what you have shown me. You will need to debug further to figure out why. You can use the React Dev Tools to inspect props of your components. You can use Redux Dev Tools to inspect and manipulate the redux state. Check what the value of auth.token is in state. Look where it is supposed to be set and find out why it isn't getting set to a valid value.
Be sure to check this article for tips on how to debug your code.

React/Redux: Why can data not be rendered when working with isFetching flag?

I'm trying to implement an isFetching flag that indicates when my data is ready for rendering. But even if the flag works, i.e. jumps from isFetching = true to isFetching = false after the data has been successfully requested, there is still an error when I try to access data: cannot read property 'username' of null
Profile Component
class Profile extends React.Component {
render() {
const (isFetching, profile) = this.props.profile
console.log (isFetching)
console.log (profile)
return <h1>Hello, {isFetching = "false"? profile[0].username : null}</h1>;
}
}
function mapStateToProps(state, ownProps) {
const profile= state.profile
return { profile }
};
export default connect(
mapStateToProps,
{ logout }
)(Profile);
Action
export const getProfile = () => (dispatch, getState) => {
// Profile Loading
dispatch({ type: GET_PROFILE_REQUEST });
axios
.get(apiBase + "/profile/", tokenConfig(getState))
.then(res => {
dispatch({
type: GET_PROFILE_SUCCESS,
payload: res.data
});
})
.catch((err) => {
dispatch(returnErrors(err.response.data, err.response.status));
dispatch({
type: GET_PROFILE_FAILURE,
});
});
};
Reducer
const initialState = {
isFetching: false,
profile: null
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_PROFILE_REQUEST:
return {
...state,
isFetching: true
};
case GET_PROFILE_SUCCESS:
return {
...state,
profile: action.payload,
isFetching: false
};
case GET_PROFILE_FAILURE:
return {
...state,
profile: action.payload,
isFetching: false
};
default:
return state;
}
}
Redux log for GET_PROFILE_SUCCESS
profile
isFetching: false
profile[
{
"username": "Daniel",
"id": 1,
"profile": {
"image": "Test",
"bio": "Test"
}
}
]
I'm happy for every clarification.
You have a small error in your code.
return <h1>Hello, {isFetching = "false"? profile.username : null}</h1>;
You are not checking for the value of isFetching but rather setting it again. Also, since profile is an array, you need to get the first element.Replace it with
return <h1>Hello, {!isFetching? profile[0].username : null}</h1>;
and it should work.

Api state control in redux i.e PENDING, SUCCESS, FAILURE in react-redux jhipster generated code

In the below jhipster generated code, how the pending, success and failure of actions are being triggered? For each action type we use, it is being appended with _PENDING or _SUCCESS or _FAILURE and I'm not able to figure out where and how it happens.
As I see pending, success and failure states are being handled by the reducer I don't understand when and where those actions are being triggered.
For example in the code below, the first action has type ACTION_TYPES.FETCH_MEDICINE_LIST = 'medicine/FETCH_MEDICINE_LIST'.
The actions that actually gets triggered are medicine/FETCH_MEDICINE_LIST_PENDING, medicine/FETCH_MEDICINE_LIST_SUCCESS, medicine/FETCH_MEDICINE_LIST_FAILURE when medicine/FETCH_MEDICINE_LIST action gets trigger. Where and how the Api state actions are being triggered?
import { ICrudGetAction, ICrudGetAllAction, ICrudPutAction, ICrudDeleteAction } from 'react-jhipster';
import { cleanEntity } from 'app/shared/util/entity-utils';
import { REQUEST, SUCCESS, FAILURE } from 'app/shared/reducers/action-type.util';
import { IMedicine, defaultValue } from 'app/shared/model/medicine.model';
export const ACTION_TYPES = {
FETCH_MEDICINE_LIST: 'medicine/FETCH_MEDICINE_LIST',
FETCH_MEDICINE: 'medicine/FETCH_MEDICINE',
CREATE_MEDICINE: 'medicine/CREATE_MEDICINE',
UPDATE_MEDICINE: 'medicine/UPDATE_MEDICINE',
DELETE_MEDICINE: 'medicine/DELETE_MEDICINE',
RESET: 'medicine/RESET'
};
const initialState = {
loading: false,
errorMessage: null,
entities: [] as ReadonlyArray<IMedicine>,
entity: defaultValue,
updating: false,
updateSuccess: false
};
export type MedicineState = Readonly<typeof initialState>;
// Reducer
export default (state: MedicineState = initialState, action): MedicineState => {
switch (action.type) {
case REQUEST(ACTION_TYPES.FETCH_MEDICINE_LIST):
case REQUEST(ACTION_TYPES.FETCH_MEDICINE):
return {
...state,
errorMessage: null,
updateSuccess: false,
loading: true
};
case REQUEST(ACTION_TYPES.CREATE_MEDICINE):
case REQUEST(ACTION_TYPES.UPDATE_MEDICINE):
case REQUEST(ACTION_TYPES.DELETE_MEDICINE):
return {
...state,
errorMessage: null,
updateSuccess: false,
updating: true
};
case FAILURE(ACTION_TYPES.FETCH_MEDICINE_LIST):
case FAILURE(ACTION_TYPES.FETCH_MEDICINE):
case FAILURE(ACTION_TYPES.CREATE_MEDICINE):
case FAILURE(ACTION_TYPES.UPDATE_MEDICINE):
case FAILURE(ACTION_TYPES.DELETE_MEDICINE):
return {
...state,
loading: false,
updating: false,
updateSuccess: false,
errorMessage: action.payload
};
case SUCCESS(ACTION_TYPES.FETCH_MEDICINE_LIST):
return {
...state,
loading: false,
entities: action.payload.data
};
case SUCCESS(ACTION_TYPES.FETCH_MEDICINE):
return {
...state,
loading: false,
entity: action.payload.data
};
case SUCCESS(ACTION_TYPES.CREATE_MEDICINE):
case SUCCESS(ACTION_TYPES.UPDATE_MEDICINE):
return {
...state,
updating: false,
updateSuccess: true,
entity: action.payload.data
};
case SUCCESS(ACTION_TYPES.DELETE_MEDICINE):
return {
...state,
updating: false,
updateSuccess: true,
entity: {}
};
case ACTION_TYPES.RESET:
return {
...initialState
};
default:
return state;
}
};
const apiUrl = 'api/medicines';
// Actions
export const getEntities: ICrudGetAllAction<IMedicine> = (page, size, sort) => ({
type: ACTION_TYPES.FETCH_MEDICINE_LIST,
payload: axios.get<IMedicine>(`${apiUrl}?cacheBuster=${new Date().getTime()}`)
});
export const getEntity: ICrudGetAction<IMedicine> = id => {
const requestUrl = `${apiUrl}/${id}`;
return {
type: ACTION_TYPES.FETCH_MEDICINE,
payload: axios.get<IMedicine>(requestUrl)
};
};
export const createEntity: ICrudPutAction<IMedicine> = entity => async dispatch => {
const result = await dispatch({
type: ACTION_TYPES.CREATE_MEDICINE,
payload: axios.post(apiUrl, cleanEntity(entity))
});
dispatch(getEntities());
return result;
};
export const updateEntity: ICrudPutAction<IMedicine> = entity => async dispatch => {
const result = await dispatch({
type: ACTION_TYPES.UPDATE_MEDICINE,
payload: axios.put(apiUrl, cleanEntity(entity))
});
dispatch(getEntities());
return result;
};
export const deleteEntity: ICrudDeleteAction<IMedicine> = id => async dispatch => {
const requestUrl = `${apiUrl}/${id}`;
const result = await dispatch({
type: ACTION_TYPES.DELETE_MEDICINE,
payload: axios.delete(requestUrl)
});
dispatch(getEntities());
return result;
};
export const reset = () => ({
type: ACTION_TYPES.RESET
});
The actions are triggered by redux-promise-middleware.
For an action FOO with an asynchronous payload, redux-promise-middleware will dispatch 3 actions:
FOO_PENDING, immediately
FOO_FULFILLED, once the promise is settled
FOO_REJECTED, if the promise is rejected
REQUEST, SUCCESS and FAILURE are just 3 simple functions in JHispter to facilitate the use of redux-promise-middleware.
export const REQUEST = actionType => `${actionType}_PENDING`;
export const SUCCESS = actionType => `${actionType}_FULFILLED`;
export const FAILURE = actionType => `${actionType}_REJECTED`;

Resources