I am using redux to maintain my state in my react app, in my functional component i am dispatching some action in following way,
const handleClickOpen = () => {
console.log('in handle');
let data = {
reports_interval : currentSetting
};
dispatch(updateEmailSettingsAction({data: data, id: settings.id}))
};
in actions i have updateEmailSettingsAction and updateEmailSettingsActionSuccess showEmailSuccessAction which looks like following.
export const updateEmailSettingsAction = (settings) => {
console.log('in actrions');
return {
type: UPDATE_EMAIL_SETTINGS,
payload: settings
};
};
export const updateEmailSettingsActionSuccess = (settings) => {
console.log('success dispatched');
return {
type: UPDATE_EMAIL_SETTINGS_SUCCESS,
payload: settings
};
};
export const showEmailSuccessAction = (message) => {
return {
type: SHOW_EMAIL_SETTINGS_SUCCESS,
payload: message
}
};
Following are my sagas
const updateEmailSettings_request = async (data, id) =>
await updateEmailSettingsService(data, id)
.then(settings => settings)
.catch(error => error);
function* updateEmailSettingsFunction(payload) {
console.log('in func');
const {data, id} = payload.payload;
try {
const req = yield call(updateEmailSettings_request, data, id);
if (req.status === 200) {
console.log('in 200');
yield put(updateEmailSettingsActionSuccess(req.data));
yield put(showEmailSuccessAction('Success'));
}
else {
if (!req.data) {
yield put(showEmailSettingsAlert(req.message))
}else {
for (let key in req.data) {
yield put(showEmailSettingsAlert(req.data[key]));
}
}
}
} catch (error) {
yield put(showEmailSettingsAlert(error));
}
}
export function* updateEmailSettings() {
console.log('in final');
yield takeLatest(UPDATE_EMAIL_SETTINGS, updateEmailSettingsFunction)
}
and in following are my reducers.
const INIT_STATE = {
alertMessage: '',
settings: null,
successMessage: '',
showEmailSuccess: false,
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case UPDATE_EMAIL_SETTINGS_SUCCESS: {
return {
...state,
loader: false,
settings: action.payload,
}
}
case SHOW_EMAIL_SETTINGS_SUCCESS: {
console.log('in here reducer');
return {
...state,
loader: false,
showEmailSuccess: true,
successMessage: action.payload
}
}
}
i am accessing this showEmailSuccess in my component usinf useSelector in and showing success message in following way
{(showEmailSuccess && NotificationManager.success(successMessage) && show)}
<NotificationContainer/>
everything is working fine but this notification container is being shown twice, i have been stuck in this for quite sometime now but can't understand why.any help is appriciated.
case UPDATE_EMAIL_SETTINGS_SUCCESS: {
return {
...state,
loader: false,
showEmailSuccess: false, // Add this line
settings: action.payload,
}
}
Related
could you please help with setting state in useContext ?
I am trying to send video variable through useEffect to setMediaContent to update mediaContext.media object. My goal is to have several media(video,images,posters) objects in media object, e.g.
https://codesandbox.io/s/friendly-sunset-o67nvj?file=/src/context.js
Thanks in advance
Try using a reducer:
import { createContext, useReducer } from "react";
// default state
const contextDefaultValues = {
video: { url: "", title: "", shown: false },
openVideo: () => {},
closeVideo: () => {},
mediaContent: { media: {}, title: "most" },
setMediaContent: () => {},
};
const MainReducer = (state = contextDefaultValues, action) => {
const { type, payload } = action;
switch (type) {
case "setMediaContent": {
const { media, title } = payload;
return { ...state, media: { ...state.media, ...media }, title: title };
}
case "closeVideo": {
return { ...state, shown: false };
}
case "openVideo": {
const { url, title } = payload;
return { ...state, url, title, shown: true };
}
default: {
throw new Error(`Unhandled action type: ${type}`);
}
}
};
export const MainContext = createContext(contextDefaultValues);
// provider recuder
const MainProvider = ({ children }) => {
const [state, dispatch] = useReducer(MainReducer, contextDefaultValues);
const openVideo = (url, title) => {
dispatch({ type: "openVideo", payload: { url, title, shown: true } });
};
const closeVideo = () => {
dispatch({ type: "closeVideo", payload: { shown: false } });
};
const setMediaContent = (media, title) => {
dispatch({ type: "setMediaContent", payload: { media, title } });
};
return (
<MainContext.Provider
value={{ ...state, setMediaContent, closeVideo, openVideo }}
>
{children}
</MainContext.Provider>
);
};
export default MainProvider;
Based on the provided sandbox, You have the render of the provider wrapped in the setMediaContent function.
Look at the { and } at line 36 and 58.
Code screenshot with misplaced brackets
I ask for help with the Redux-saga, namely with refactoring the code below. Any ideas or explanations are welcome. The code below gets a list of hotels from the API on request with parameters from the function. It also has a check to see if data is currently being loaded or not at all. If the data is received, the action creator set Hotels is successfully executed. Thanks in advance for your reply.
hotels.js
export const getHotels = (cityName = 'London', date = currentDate(), days = 1, limit = 30) => {
return async (dispatch) => {
dispatch(setIsFetching(true))
dispatch(setIsEmpty(false))
try {
const response = await axios.get(`http://engine.hotellook.com/api/v2/cache.json?location=${cityName}¤cy=rub&checkIn=${date}&checkOut=${addDays(date, days)}&limit=${limit}`)
response.data.length === 0 ? dispatch(setIsEmpty(true)) : dispatch(setHotels(response.data))
}
catch (e) {
dispatch(setIsEmpty(true))
}
}
hotelsReducer.js
const SET_HOTELS = "SET_HOTELS";
const SET_IS_FETCHING = "SET_IS_FETCHING";
const SET_IS_EMPTY = "SET_IS_EMPTY";
const defaultState = {
hotels: [],
isFetching: true,
isEmpty: false,
};
export const hotelsReducer = (state = defaultState, action) => {
switch (action.type) {
case SET_HOTELS:
return {
...state,
hotels: action.payload,
isFetching: false,
};
case SET_IS_FETCHING:
return {
...state,
isFetching: action.payload,
};
case SET_IS_EMPTY:
return {
...state,
isEmpty: action.payload,
};
default:
return state;
}
};
export const setHotels = (results) => {return { type: SET_HOTELS, payload: results }};
export const setIsFetching = (bool) => {return { type: SET_IS_FETCHING, payload: bool }};
export const setIsEmpty = (bool) => {return { type: SET_IS_EMPTY, payload: bool }};
The saga is going to be very similar, you just need to replace the thunk with a new action that will trigger the saga:
import { put, takeLatest } from "redux-saga/effects";
function* fetchHotelsSaga() {
yield put(setIsFetching(true));
yield put(setIsEmpty(false));
try {
const response = yield axios.get(`http://engine.hotellook.com/api/v2/cache.json?location=${cityName}¤cy=rub&checkIn=${date}&checkOut=${addDays(date, days)}&limit=${limit}`);
response.data.length === 0
? yield put(setIsEmpty(true))
: yield put(setHotels(response.data));
} catch (e) {
yield put(setIsEmpty(true));
}
}
function* hotelsSaga() {
// FETCH_HOTELS is a type for the new action that will be dispatched instead of the thunk
yield takeLatest(FETCH_HOTELS, fetchHotelsSaga);
}
I'm working on a MERN Stack. The database is posting to the route correctly, but the reducer is not receiving the action when trying to read all the database entries. It's possible that the action readAllEmployment() is not being hit by the front end correctly, but the information does render in PostMan.
index.js
import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import { Carousel } from '../../components'
import { readAllEmployment } from '../../actions'
import './_resumeRender.scss'
const ResumeRender = () => {
useEffect(() => {
console.log('Hit Use Effect')
readAllEmployment()
}, [])
return <></>
}
const mapStateToProps = (state) => ({
resumeEmploymentReducer: state.resumeEmploymentReducer,
})
export default connect(mapStateToProps)(ResumeRender)
route.js
// load Model
const employmentModel = require('../models/employmentModel')
// #Route GET api/employment/
// #Desc Read All Employment
// #Action readAllEmployment()
// #Access Private
router.get('/', async (req, res) => {
console.log('readAllEmployment Route')
try {
const employment = await employmentModel.find().sort('endDate')
if (employment.length <= 0) {
return res.status(400).json({
errors: [{ msg: 'No employment was found' }],
})
}
return res.json(employment)
} catch (err) {
console.error(err.message)
return res.status(500).send('Server Error')
}
})
reducer.js
import {
GET_ALL_EMPLOYMENT,
GET_ONE_EMPLOYMENT,
DELETE_EMPLOYMENT,
RESET_EMPLOYMENT,
EMPLOYMENT_LOADING,
EMPLOYMENT_FAIL,
EMPLOYMENT_SUCCESS,
} from '../actions'
const resumeEmploymentReducer = (
state = {
allEmployment: [], // Pulls in all employment
employment: null, // Pulls in Specific employment
loading: false, // Has everything need been loaded
success: {},
error: {},
},
action,
) => {
const { type, payload } = action
switch (type) {
case GET_ALL_EMPLOYMENT:
console.log('GET_ALL_EMPLOYMENT Reducer')
return {
...state,
allEmployment: payload,
loading: false,
}
case GET_ONE_EMPLOYMENT:
return {
...state,
employment: payload,
loading: false,
}
case DELETE_EMPLOYMENT:
return {
...state,
allEmployment: payload,
loading: false,
}
case RESET_EMPLOYMENT:
return {
...state,
employment: null,
loading: false,
}
case EMPLOYMENT_LOADING:
return {
...state,
loading: true,
employment: null,
error: {},
}
case EMPLOYMENT_FAIL:
return {
...state,
error: payload,
allEmployment: [],
employment: null,
loading: false,
}
case EMPLOYMENT_SUCCESS:
return {
...state,
success: payload,
}
default:
return state
}
}
export default resumeEmploymentReducer
action.js
export const GET_ALL_EMPLOYMENT = 'GET_ALL_EMPLOYMENT'
export const GET_ONE_EMPLOYMENT = 'GET_ONE_EMPLOYMENT'
export const DELETE_EMPLOYMENT = 'ELETE_EMPLOYMENT'
export const RESET_EMPLOYMENT = 'RESET_EMPLOYMENT'
export const EMPLOYMENT_LOADING = 'EMPLOYMENT_LOADING '
export const EMPLOYMENT_FAIL = 'EMPLOYMENT_FAIL'
export const EMPLOYMENT_SUCCESS = 'EMPLOYMENT_SUCCESS'
// #Route GET api/employment
// #Desc Read All Employment
// #Action readAllEmployment()
// #Access Private
export const readAllEmployment = () => async (dispatch) => {
console.log('readAllEmployment Action')
try {
const res = await axios.get('/api/employment/')
dispatch({
type: GET_ALL_EMPLOYMENT,
payload: res.data,
})
} catch (err) {
if (err.response.data.errors) {
dispatch({
payload: { msg: err.response.statusText, status: err.response.status },
})
}
dispatch({
type: EMPLOYMENT_FAIL,
payload: { msg: err.response.statusText, status: err.response.status },
})
}
}
Redux DevTools
resumeEmploymenrReducer
allEmployment: []
employment: null
loading: false
success: { }
error: { }
console
Hit Use Effect
terminal
[1] Compiled successfully!
[0] Server is running on port 6060
[0] Database connected!
[0] readAllEmployment Route
PostMan
GET: http://localhost:6060/api/employment/
BODY RETURNS
[
{
"_id": "614b517cbc3fdc6d0d82ec4d",
"title": "Job Title",
"employmentType": "Full-Time",
"company": "Compnay Name",
"locationCity": "City",
"locationState": "State",
"startDate": "01-01-2021",
"endDate": "01-01-2021",
"description": "Description",
"__v": 0
}
]
I think you might need to utilize useDispatch from react-redux library.
import { useDispatch } from 'react-redux';
import { readAllEmployment } from '../../actions';
const ResumeRender = () => {
const dispatch = useDispatch()
useEffect(() => {
console.log('Hit Use Effect')
dispatch(readAllEmployment())
}, [])
return <></>
}
export default ResumeRender
I'm a PHP/Laravel mid-level developer. I'm totally a noob when it comes to react.js. Now I know that react uses API's and stuff to show data when it comes to laravel backend. I am used to traditional HTML, CSS, JS, Bootstrap, Ajax and whatsoever. I have a simple task to login a user from react through laravel backend. I'v created the APIs for that task and they're working totally fine and somehow I got lucky and attached those APIs ALL BY MYSELF (with a little research, of course). Now whenever I try to signin, I receive the usual data through an axios request to backend and have it in a variable signInUser. Now when I try to pass that data to other action function it's going undefined somehow. Here's my code so that you can understand what I'm trying to achieve:
Components\SignIn.js
constructor() {
super();
this.state = {
email: '',
password: ''
}
}
componentDidUpdate() {
if (this.props.showMessage) {
setTimeout(() => {
this.props.hideMessage();
}, 100);
}
if (this.props.authUser !== null) {
this.props.history.push('/');
}
}
render() {
const {email, password} = this.state;
const {showMessage, loader, alertMessage} = this.props;
return (
// other components and stuff...
<Button onClick={() => { this.props.showAuthLoader(); this.props.userSignIn({email, password});}} variant="contained" color="primary">
<IntlMessages id="appModule.signIn"/>
</Button>
);
}
const mapStateToProps = ({auth}) => {
const {loader, alertMessage, showMessage, authUser} = auth;
return {loader, alertMessage, showMessage, authUser}
};
export default connect(mapStateToProps, {
userSignIn,
hideMessage,
showAuthLoader
})(SignIn);
Sagas\Auth.js
const signInUserWithEmailPasswordRequest = async (email, password) =>
await axios.post('auth/login', {email: email, password: password})
.then(authUser => authUser)
.catch(err => err);
function* signInUserWithEmailPassword({payload}) {
const {email, password} = payload;
try {
const signInUser = yield call(signInUserWithEmailPasswordRequest, email, password);
if (signInUser.message) {
yield put(showAuthMessage(signInUser.message));
} else {
localStorage.setItem('user_id', signInUser.data.user.u_id);
yield put(userSignInSuccess(signInUser.data.user.u_id));
}
} catch (error) {
yield put(showAuthMessage(error));
}
}
export function* signInUser() {
yield takeEvery(SIGNIN_USER, signInUserWithEmailPassword);
}
export default function* rootSaga() {
yield all([fork(signInUser),
// couple of other functions...
);
}
actions\Auth.js
export const userSignIn = (user) => {
return {
type: SIGNIN_USER,
payload: user
};
};
export const userSignInSuccess = (authUser) => {
console.log(authUser); // It's printing undefined, I don't know why?!
return {
type: SIGNIN_USER_SUCCESS,
payload: authUser
}
};
reducers\Auth.js
const INIT_STATE = {
loader: false,
alertMessage: '',
showMessage: false,
initURL: '',
authUser: localStorage.getItem('user_id'),
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case SIGNIN_USER_SUCCESS: {
return {
...state,
loader: false,
authUser: action.payload
}
}
case INIT_URL: {
return {
...state,
initURL: action.payload
}
}
default:
return state;
}
}
P.s: It's a purchased react.js template (not my code).
In my component I want to check when the parameter has changed and update accordingly. However when I do this, I am seeing weird behaviour and multiple requests been made to my api.
my component:
componentWillMount() {
this.state = {
path: this.props.match.params.categoryName,
};
}
componentDidUpdate(prevProps) {
if (prevProps === undefined) {
return false;
}
if (this.state.path !== this.props.match.params.categoryName) {
this.getCategory()
}
}
getCategory() {
if (this.props.allPosts && this.props.allPosts.length) {
const categoryId = _.result(_.find(this.props.allPosts, v => (
v.name === this.props.match.params.categoryName ? v.id : null
)), 'id');
this.props.dispatch(Actions.fetchCategory(categoryId));
}
}
my action:
import Request from 'superagent';
import Constants from '../constants';
const Actions = {
fetchCategory: categoryId => (dispatch) => {
dispatch({ type: Constants.CATEGORY_FETCHING });
Request.get(`/api/v1/categories/${categoryId}`)
.then((data) => {
dispatch({
type: Constants.CATEGORY_RECEIVED,
category: { id: data.body.id, name: data.body.name },
category_posts: data.body.posts,
});
});
},
};
export default Actions;
my reducer:
import Constants from '../constants';
const initialState = {
posts: [],
category: [],
fetching: true,
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case Constants.CATEGORY_FETCHING:
return Object.assign({}, state, { fetching: true });
case Constants.CATEGORY_RECEIVED:
return Object.assign({}, state, { category: action.category,
posts: action.category_posts,
fetching: false });
default:
return state;
}
}