how to update and delete withdout reloading the page - reactjs

I'm trying to update and delete some notes that are on cloud firestore via redux, every thing seems to work fine but i should reload the page to see the results
import { combineReducers } from 'redux';
import authReducer from './authReducer';
import noteReducer from './noteReducer';
import { firestoreReducer } from 'redux-firestore';
import { firebaseReducer } from 'react-redux-firebase';
const rootReducer = combineReducers({
auth: authReducer,
note: noteReducer,
firestore: firestoreReducer,
firebase: firebaseReducer
});
export default rootReducer;
const store = createStore(rootReducer,
composeEnhancers(
applyMiddleware(thunk.withExtraArgument({getFirebase, getFirestore})),
reduxFirestore(fbConfig),
reactReduxFirebase(fbConfig, {useFirestoreForProfile: true, userProfile: 'users', attachAuthIsReady: true})
)
);
this is my note action:
export const createNote = (note) => {
return (dispatch, getState, {getFirebase, getFirestore}) => {
const firestore = getFirestore();
const profile = getState().firebase.profile;
const authorId = getState().firebase.auth.uid;
firestore.collection('notes').add({
...note,
authorFirstName: profile.firstName,
authorLastName: profile.lastName,
authorId: authorId,
createdAt: new Date(),
})
.then(() => {
dispatch({type: 'CREATE_NOTE_SUCCESS', note});
})
.catch((err) => {
dispatch({type: 'CREATE_NOTE_ERROR', err});
})
};
};
export const updateNote = (updatedNote, id) => {
return (dispatch, getState, { getFirestore }) => {
const firestore = getFirestore();
firestore.collection('notes').doc(id.toString()).update({
...updatedNote,
createdAt: new Date(),
})
.then(() => {
dispatch({type: 'UPDATE_NOTE_SUCCESS'}, updatedNote)
})
.catch(() => {
dispatch({type: 'UPDATE_NOTE_ERROR'})
})
};
};
export const deleteNote = (id) => {
return (dispatch, getState, { getFirestore }) => {
const firestore = getFirestore();
firestore.collection('notes').doc(id.toString()).delete()
.then(() => {
dispatch({type: 'DELETE_NOTE_SUCCESS'}, id);
})
.catch(() => {
dispatch({type: 'DELETE_NOTE_ERROR'})
});
};
};
this is my notereducer
import * as actionTypes from '../actions/actionTypes';
import { updateObject } from '../../../shared/utility';
const initState = {
notes: [],
id: null,
error: null,
changed: false
};
const noteReducer = (state = initState, action) => {
switch (action.type) {
case actionTypes.CREATE_NOTE_SUCCESS:
return updateObject(state, {error: null , changed: true})
case actionTypes.CREATE_NOTE_ERROR:
return updateObject(state, {error: action.err});
case actionTypes.UPDATE_NOTE_SUCCESS:
return updateObject(state, {error: null, changed: true});
case actionTypes.UPDATE_NOTE_ERROR:
return updateObject(state, {error: action.err});
case actionTypes.DELETE_NOTE_SUCCESS:
return updateObject(state, {
notes: state.notes.filter(note => note.id !== action.id),
error: null,
changed: true
});
case actionTypes.DELETE_NOTE_ERROR:
return updateObject(state, {error: action.err});
default:
return state;
}
};
export default noteReducer;
here are some parts of my code, i don't know what part I've done wrong or what I've missed, i really appreciate your help

Related

React-redux- Fetching data from API

I am working on a project and I need to fetch data from backend or from an API. I tried fetch the data but nothing appears. I think I am doing something wrong in the container. I am a beginner in react-redux, I don't know what I am doing wrong.
I've already read all the posts but nothing seems to works.
my reducer:
const initialState={
articles: [],
};
const rootReducer = (state = initialState, action) => {
const { type, payload }=action;
switch(type) {
case SRETRIEVE_ARTICLE:{
return {
...state,
articles:payload,
};
}
default: return state;
}
}
export default rootReducer;
This is what I have right now in container:
import Articles from 'components/Articles';
import { fetchArticles } from '../../pages/index';
const mapStateToProps = (state) => ({
articles:state.articles
})
const ConnectedArticles = connect(
mapStateToProps,
{fetchArticles}
)(Articles)
export default ConnectedArticles;
pages.js
axios.get('API').then((response) => {
const { data } = response;
dispatch({ type: RETRIEVE_ARTICLES, payload: data });
});
};
const Index = () => {
const articles= useSelector((state) => state.articles);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchArticles);
}, []);
return <>{articles && articles.map((article) => <Article key={article.id} name={article.name} />)}</>;
};
Index.getInitialProps = async () => ({
authRequired: true,
label: 'Dashboard',
});
export default Index;
Also I defined the action type: export const SET_UNOPENED_REWARD = 'SET_UNOPENED_REWARD';
and action const unopenedRewards = (payload) => ({ type: SET_UNOPENED_REWARD, payload });
One very nice way to do data fetching with redux is to use redux toolkit's createAsyncThunk and createSlice functions.
// src/features/articles/articlesSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const fetchArticles = createAsyncThunk("articles/get", async () => {
// Here you can use axios with your own api
const response = await fetch("https://rickandmortyapi.com/api/character");
const json = await response.json();
return json.results;
});
export const slice = createSlice({
name: "articles",
initialState: {
loading: false,
data: []
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchArticles.pending, (state) => {
state.loading = true;
});
builder.addCase(fetchArticles.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
});
builder.addCase(fetchArticles.rejected, (state) => {
state.loading = false;
});
}
});
export default slice.reducer;
// src/features/articles/Articles.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchArticles } from "./articlesSlice";
export const Articles = () => {
const articles = useSelector((state) => state.articles.data);
const loading = useSelector((state) => state.articles.loading);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchArticles());
}, []);
return (
<>
{loading && "...loading"}
{articles.map((article) => <Article key={article.id} {...article} />)}
</>
);
};
you should use async and await
let response = await axios.get('https://run.mocky.io/v3/5c045896-3d18-4c71-a4e5-5ed32fbbe2de')
if(response.status==200){
dispatch({ type: RETRIEVE_ARTICLES, payload: data });
}

TypeError: Cannot read property 'categories' of undefined

i'm trying to solve this problem for 2 days but I didn't got the solution till now, can anyone help me,
the categories success rendering in the console and redux normally with the data but not render on the screen
Category Screen
const category = useSelector(state => state.category);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getCategory());
}, [dispatch]);
const renderCategories = (categories) => {
let myCategories = [];
for (let category of categories) {
myCategories.push(
<li key={category.name}>
{category.name}
{/* {category.children.length > 0 ? (<ul>{renderCategories(category.children)}</ul>) : null} */}
</li>
);
}
return myCategories;
}
the return of this part (the problem is here (Cannot read property 'categories' of undefined)
<Col md={12}>
<ul>
{renderCategories(category.categories)}
</ul>
</Col>
Reducer
const initState = {
categories: [],
loading: false,
error: null
};
export const getCategoryReducer = (state = initState, action) => {
switch (action.type) {
case GET_CATEGORY_SUCCESS:
return { ...state, categories: action.payload.categories };
default:
return state;
}
};
the Get Action
export const getCategory = () => {
return async dispatch => {
dispatch({ type: GET_CATEGORY_REQUEST })
const res = await axios.get(`api/category/getcategory`);
console.log(res);
if (res.status === 200) {
const { categories } = res.data;
dispatch({ type: GET_CATEGORY_SUCCESS, payload: { categories: categories } })
} else {
dispatch({ type: GET_CATEGORY_FAIL, payload: { error: res.data.error } })
}
}
}
store
import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { getCategoryReducer } from './reducers/categoryReducers';
import {
userDeleteReducer,
userDetailsReducer,
userListReducer,
userRegisterReducer,
userSigninReducer,
userUpdeteProfileReducer,
userUpdeteReducer,
} from './reducers/userReducers';
const initialState = {
userSignin: {
userInfo: localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null,
},
};
const reducer = combineReducers({
userSignin: userSigninReducer,
userRegister: userRegisterReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdeteProfileReducer,
userUpdate: userUpdeteReducer,
userList: userListReducer,
userDelete: userDeleteReducer,
getCategory: getCategoryReducer,
});
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancer(applyMiddleware(thunk))
);
export default store;
Router in the backend
categoryRouter.get(
'/getcategory',
(req, res) => {
Category.find({}).exec((error, categories) => {
if (error) return res.status(400).json({ error });
if (categories) {
res.status(200).json({ categories });
}
});
});
export default categoryRouter;
category model
import mongoose from 'mongoose';
const categorySchema = new mongoose.Schema(
{
name: { type: String, required: true, trim: true },
slug: { type: String, required: true, unique: true },
parentId: { type: String }
}, { timestamps: true }
);
const Category = mongoose.model('Category', categorySchema);
export default Category;
here it's the issue:
const reducer = combineReducers({
userSignin: userSigninReducer,
userRegister: userRegisterReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdeteProfileReducer,
userUpdate: userUpdeteReducer,
userList: userListReducer,
userDelete: userDeleteReducer,
getCategory: getCategoryReducer,
});
your combine reducers creates a getCategory, not a category. In order to proper extract it with useSelector you should change it:
const category = useSelector(state => state.getCategory);
or rename at your combineReducers:
const reducer = combineReducers({
userSignin: userSigninReducer,
userRegister: userRegisterReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdeteProfileReducer,
userUpdate: userUpdeteReducer,
userList: userListReducer,
userDelete: userDeleteReducer,
category: getCategoryReducer,
});
category can be undefined until the state was full filled. So you can try it this way:
{renderCategories(category?.categories)}

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.

Can't get user description in Redux + Firebase

Although I have successfully fetched users and profiles but I don't know why this error below occurs:
Cannot read property 'content' of undefined
{content: "Hello world", status: 2}
Initially, I use Firestore and this time fetch 2 docs from different collections, users and profiles.
redux code:
export function fetchUser(id) {
return dispatch => {
usersRef
.doc(id)
.get()
.then(doc => {
profilesRef
.where("uid", "==", id)
.where("status", "==", doc.data().current_status)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(snapshot) {
const profileItems = { ...doc.data(), profiles: snapshot.data() };
dispatch({
type: FETCH_USER,
payload: profileItems
});
});
});
});
};
}
In container:
import { FETCH_USER } from "../actions";
const initialState = {};
export default (state = initialState, action) => {
switch (action.type) {
console.log("action", action.payload)
// successfully fetched, user info and profiles: profiles info
case FETCH_USER:
return action.payload;
default:
return state;
}
};
In view:
function mapStateToProps({ users }) {
console.log("content", user.profiles.content)
// Cannot read property 'content' of undefined
return { user: users, profiles: user.profiles };
}
reducer file:
import { createStore, applyMiddleware, combineReducers } from "redux";
import UsersReducer from "./reducer_users.js";
const rootReducer = combineReducers({
users: UsersReducer,
});
const store = createStore(rootReducer, applyMiddleware(reduxThunk));
export default store;
Here you are making mistack you are accessing user instead of users
function mapStateToProps({ users , content }) {
console.log("content", user.profiles.content) // change user to users
console.log("content",content) // you can directly access content here
return { user: users, profiles: user.profiles };
}
you can initialize default state here
add one more type FETCH_USER_COMPLETE
import { FETCH_USER } from "../actions";
const initialState = {
content : '',
users : ''
};
export default (state = initialState, action) => {
switch (action.type) {
console.log("action", action.payload)
// successfully fetched, user info and profiles: profiles info
case FETCH_USER:
return { ...state , action.payload };
case FETCH_USER_COMPLETE :
return { ...state , users : action.payload.users , content : action.payload.users.profiles.content };
default:
return state;
}
};
I think it might not be best practice but I have solved like this:
from:
querySnapshot.forEach(function(snapshot) {
const profileItems = { ...doc.data(), profiles: snapshot.data() };
dispatch({
type: FETCH_USER,
payload: profileItems
});
});
to:
querySnapshot.forEach(function(snapshot) {
const userItems = { ...doc.data(), ...snapshot.data() };
dispatch({
type: FETCH_USER,
payload: userItems
});
});
I can get content like {user.content}.

Redux importing side effect returns Reducer Returned Undefined

I have the following redux module:
import { Map, fromJS } from 'immutable'
import api from '../lib/api'
import config from '../config/application'
import storage from '../lib/storage'
import { history } from '../lib/create-store'
// Actions
const USER_LOGIN = 'ts/user/USER_LOGIN'
const USER_LOGIN_RESPONSE = 'ts/user/USER_LOGIN_RESPONSE'
const USER_LOGOUT = 'ts/user/USER_LOGOUT'
const CREATE_ACCOUNT_TEAM = 'ts/user/CREATE_ACCOUNT_TEAM'
const CREATE_ACCOUNT_TEAM_RESPONSE = 'ts/user/CREATE_ACCOUNT_TEAM_RESPONSE'
const RECEIVE_ACCESS_TOKEN = 'ts/user/RECEIVE_ACCESS_TOKEN'
const RECEIVE_USER = 'adsdean/user/RECEIVE_USER'
const initialState = Map({
accessToken: null,
loginPending: false,
loginError: false,
creatingAccountTeam: false,
creatingAccountTeamSuccess: false,
creatingAccountTeamError: Map({}),
profile: Map({
id: null,
email: null,
firstName: null,
lastName: null,
company: null,
mobile: null,
mobileShare: true,
dob: null
})
})
// Reducer
export default function user (state = initialState, action = {}) {
switch (action.type) {
case RECEIVE_ACCESS_TOKEN: {
storage.save('key', action.token)
api.setAuth(action.token)
return state.set('accessToken', fromJS(action.token))
}
case USER_LOGIN:
return state.set('loginPending', true)
case USER_LOGIN_RESPONSE: {
let nextState = state.set('loginPending', false)
if (action.status === 'success') {
nextState = nextState
.set('loginError', initialState.loginError)
} else {
nextState = nextState.set('loginError', action.error)
}
return nextState
}
case USER_LOGOUT: {
storage.delete('key')
api.setAuth(null)
return state.set('accessToken', null)
}
case CREATE_ACCOUNT_TEAM:
return state.set('creatingAccountTeam', true)
case CREATE_ACCOUNT_TEAM_RESPONSE: {
console.log(action)
let nextState = state.set('creatingAccountTeam', false)
if (action.status === 'success')
state.set('creatingAccountTeamSuccess', true)
else
nextState = nextState.set('creatingAccountTeamError', fromJS(action.error))
return nextState
}
case RECEIVE_USER:
return state
.setIn('profile', 'id', action.payload.id)
.setIn('profile', 'email', action.payload.email)
.setIn('profile', 'firstName', action.payload.first_name)
.setIn('profile', 'lastName', action.payload.last_name)
.setIn('profile', 'company', action.payload.company)
.setIn('profile', 'mobile', action.payload.mobile)
.setIn('profile', 'mobileShare', action.payload.mobile_share)
.setIn('profile', 'dob', action.payload.dob)
}
return state
}
// ==============================
// Action Creators
// ==============================
export const userLoginResponse = (status, error) => ({
type: USER_LOGIN_RESPONSE,
status,
error,
})
export const receiveAccessToken = token => ({
type: RECEIVE_ACCESS_TOKEN,
token
})
export const userCreateWithTeamResponse = (status, error) => ({
type: CREATE_ACCOUNT_TEAM_RESPONSE,
status,
error,
})
export const receiveUser = user => ({
type: RECEIVE_USER,
payload: user
})
export const getAccessToken = state =>
state.get('accessToken')
export const isLoggedIn = state =>
state.get('accessToken')
// ==============================
// SIDE EFFECTS
// ==============================
//
//
export const getUser = () => async dispatch => {
api.request.get('/user')
.then(response => {
dispatch(receiveUser(response.data))
})
.catch(error => {
})
}
export const userLogin = (username, password) => async dispatch => {
dispatch({ type: USER_LOGIN })
api.request.post('/oauth/token', {
username,
password,
'grant_type': 'password',
'client_id': config.clientId,
'client_secret': config.clientSecret,
})
.then(response => {
console.log(response)
const { access_token } = response.data
dispatch(userLoginResponse('success', null))
dispatch(receiveAccessToken(access_token))
dispatch(receiveUser(response.data))
window.gtag('event', 'login')
})
.catch(error => {
console.log(error)
dispatch(userLoginResponse('error', error))
})
}
export const userLogout = () => dispatch => {
dispatch({ type: USER_LOGOUT })
history.push('/')
window.gtag('event', 'logout')
//logFormSubmission('Logout')
}
export const userCreateWithTeam = (form) => async dispatch => {
dispatch({ type: CREATE_ACCOUNT_TEAM })
api.request.post('/account/register/team', {
email: form.email,
password: form.password,
'first_name': form.firstName,
'last_name': form.lastName,
company: form.company,
name: form.name,
location: form.location,
dob: form.dob,
mobile: form.mobile,
'mobile_share': (form.mobileShare === true) ? 1 : 0
})
.then(response => {
console.log(response)
dispatch(userCreateWithTeamResponse('success', null))
dispatch(userLogin(form.email, form.password))
window.gtag('event', 'create account and team')
window.gtag('set', {'user_id': response.data.id})
})
.catch(error => {
console.log(error.response)
dispatch(userCreateWithTeamResponse('error', error.response.data))
})
}
everything was working fine until I added the side effect function named getUser. As soon as I do import { getUser } from '../../modules/user' from one of my components/containers I get hit with
Error: Reducer "user" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined.
As soon as I comment out the import, everything else that uses the module continues working fine. What is it about that new side effect, that looks the same as the others that is causing this?
My create store code:
import { createStore, applyMiddleware, compose } from 'redux'
import { ConnectedRouter, routerReducer, routerMiddleware, push } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory'
import { combineReducers } from 'redux-immutablejs'
import { fromJS, Map } from 'immutable'
import thunk from 'redux-thunk'
import user from '../modules/user'
const initialState = fromJS({})
const enhancers = []
const middleware = [
thunk,
routerMiddleware(history)
]
export const history = createHistory()
const reducer = combineReducers({
user,
router: routerReducer,
})
if (process.env.NODE_ENV === 'development') {
const devToolsExtension = window.devToolsExtension
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension())
}
}
const composedEnhancers = compose(
applyMiddleware(...middleware),
...enhancers
)
const store = createStore(
reducer,
initialState,
composedEnhancers
)
export default store
I can fix the error by moving initialState Map directly into the default param part of the reducer... It seems the error is produced when having it in a separate const. However it's been working that way for weeks before the introduction of this function...
e.g
// Reducer
export default function user (state = Map({
accessToken: null,
loginPending: false,
loginError: false,
creatingAccountTeam: false,
creatingAccountTeamSuccess: false,
creatingAccountTeamError: Map({}),
profile: Map({
id: null,
email: null,
firstName: null,
lastName: null,
company: null,
mobile: null,
mobileShare: true,
dob: null
})
}), action = {}) {
setIn syntax that you have used is incorrect, the nested path needs to be inside [].
.setIn(['profile', 'id'], action.payload.id)
.setIn(['profile', 'email'], action.payload.email)
.setIn(['profile', 'firstName'], action.payload.first_name)
.setIn(['profile', 'lastName'], action.payload.last_name)
.setIn(['profile', 'company'], action.payload.company)
.setIn(['profile', 'mobile'], action.payload.mobile)
.setIn(['profile', 'mobileShare'], action.payload.mobile_share)
.setIn(['profile', 'dob'], action.payload.dob)
Check this documentation
also in
if (action.status === 'success')
state.set('creatingAccountTeamSuccess', true) . // <--
you missed to assign this to nextState which may lead to inconsistencies

Resources