Redux state overwritten after next fetch - javascript

In my web app, I want to fetch urls from an API. Also, I want to fetch categories for these items.
index.js:
componentDidMount () {
this.props.fetchUrls();
this.props.fetchCategories();
}
Im fetching the urls first like that:
export const fetchUrlsSuccess = urls => ({
type: FETCH_URLS_SUCCESS,
payload: { urls }
});
export const fetchUrls = () => dispatch => {
dispatch(fetchUrlsBegin());
return fetch(`${api}/urls`)
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchUrlsSuccess(json));
return json.urls;
})
.catch(error => dispatch(fetchUrlsFailure(error)));
};
fetching categories:
export const fetchCategoriesSuccess = categories => ({
type: FETCH_CATEGORIES_SUCCESS,
payload: { categories }
});
export const fetchCategoriesFailure = error => ({
type: FETCH_CATEGORIES_FAILURE,
payload: { error }
});
export function fetchCategories() {
return dispatch => {
dispatch(fetchCategoriesBegin());
return fetch(`${api}/categories`)
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchCategoriesSuccess(json));
return json.categories;
})
.catch(error => dispatch(fetchCategoriesFailure(error)));
};
}
url reducer:
import {
FETCH_URLS_BEGIN,
FETCH_URLS_SUCCESS,
FETCH_URLS_FAILURE
} from "../actions/types";
export default function urlReducer(state = [], action) {
switch (action.type) {
case FETCH_URLS_BEGIN:
console.log("url fetch begin", state);
return {
...state,
loading: true,
error: null
};
case FETCH_URLS_SUCCESS:
console.log("url fetch success", state);
return {
...state,
loading: false,
items: action.payload.urls
};
case FETCH_URLS_FAILURE:
console.log("url fetch error", state);
return {
...state,
loading: false,
error: action.payload.error,
items: []
};
default:
return state;
}
}
categories reducer:
import {
FETCH_CATEGORIES_BEGIN,
FETCH_CATEGORIES_SUCCESS,
FETCH_CATEGORIES_FAILURE
} from "../actions/types";
export default function categoriesReducer(state = [], action) {
switch (action.type) {
case FETCH_CATEGORIES_BEGIN:
console.log("categories fetch begin", state);
return {
...state,
loading: true,
error: null
};
case FETCH_CATEGORIES_SUCCESS:
console.log("categories fetch success", state);
return {
...state,
loading: false,
items: action.payload.categories
};
case FETCH_CATEGORIES_FAILURE:
console.log("categories fetch fail", state);
return {
...state,
loading: false,
error: action.payload.error,
items: []
};
default:
return state;
}
}
combining reducers in index of reducers:
import { combineReducers } from "redux";
import urlReducer from "./urlReducer";
import categoriesReducer from "./categoriesReducer";
import modalReducer from "./modalReducer";
export default combineReducers({
urls: urlReducer,
modal: modalReducer,
categories: categoriesReducer
});
create store :
import { createStore, applyMiddleware, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import thunk from "redux-thunk";
import storage from "redux-persist/lib/storage";
import rootReducer from "../reducers";
const persistConfig = {
key: "root",
storage
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const middleware = [thunk];
let store = createStore(
persistedReducer,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
let persistor = persistStore(store);
export { store, persistor };
For the categories, I do the same. Then I combine both reducers.
What happens is that the state.urls. Items get overwritten and state.categories.items holds state instead. I don't understand why.
output of the redux dev-tool after the second fetch:
I'm pretty new to redux and don't understand the state management...

Related

SQLite not working with Redux in React Native

After introducing Redux to my React Native Expo app, whenever I try to interact with the database my app stops working.
actions.js:
export const SET_SELECTED_PLAYERS = "SET_SELECTED_PLAYERS"
export const SET_PLAYERS = "SET_PLAYERS"
export const SET_SELECTED_COURSE = "SET_SELECTED_COURSE"
export const SET_COURSES = "SET_COURSES"
//Player actions
export const setPlayers = (players) => (
{ type: SET_PLAYERS, payload: players, }
)
export const setSelectedPlayers = (players) => (
({ type: SET_SELECTED_PLAYERS, payload: players, })
)
export const setSelectedCourse = (course) =>
({ type: SET_SELECTED_COURSE, payload: course, })
export const setCourses = (courses) =>
({ type: SET_COURSES, payload: courses, })
reducers.js:
import { SET_PLAYERS, SET_SELECTED_PLAYERS, SET_SELECTED_COURSE, SET_COURSES } from "./actions"
const initialState = {
players: [],
selectedPlayers: [],
courses: [],
selectedCourse: null,
round: {}
}
export const playerReducer = (state = initialState, action) => {
switch (action.type) {
case SET_PLAYERS:
return { ...state, players: action.payload }
case SET_SELECTED_PLAYERS:
return { ...state, selectedPlayers: action.payload }
default:
return state
}
}
export const courseReducer = (state = initialState, action) => {
switch (action.type) {
case SET_SELECTED_COURSE:
return { ...state, selectedCourse: action.payload }
case SET_COURSES:
return { ...state, courses: action.payload }
default:
return state
}
}
store.js:
import { createStore, combineReducers, applyMiddleware } from "redux";
import { courseReducer, playerReducer } from "./reducers";
const rootReducer = combineReducers({ playerReducer, courseReducer })
export const Store = createStore(rootReducer)
SQLite used in component :
const dispatch = useDispatch()
const db = SQLite.openDatabase("players.db")
useEffect(() => {
db.transaction(tx => {
tx.executeSql("SELECT * FROM Player", [], (trans, result) => {
dispatch(setPlayers(result.rows._array))
})
})
}, [])
Table for Player exists and app worked before I introduced Redux. It interacts with Firebase and when fetching data from cloud Redux has no problems. What problems could it have with SQLite?
Use sqlite query in redux action
export const getUsers = () => {
try {
return async dispatch => {
const result = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const json = await result.json();
if (json) {
dispatch({
type: GET_USERS,
payload: json
})
} else {
console.log('fetch user api error');
}
}
} catch (error) {
console.log('action error');
}
}

React - can't read property "value" of undefined

While learning redux I have come to a standstill when trying to fire an action. I am able to console log action.payload successfully, as well as {data} in the action so I suspect maybe it has something to do with local storage(?) but I really can't see what's wrong. I am able to access everything from the database correctly in other areas of the app so I don't think there is any problem with the database. Please let me know if I need to clarify anything.
Here is my reducer :
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
console.log("a.p", action.payload);
const existItem = state.cartItems.find(
(x) => x.product === item.product
);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x
),
};
} else {
return {
...state,
cartItems: [...state.cartItems, item],
};
}
And here is my action :
export const addToCart = (id, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/api/products/${id}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
});
localStorage.setItem(
"cartItems",
JSON.stringify(getState().cart.cartItems)
);
};
and store :
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import {
productListReducer,
productDetailsReducer,
} from "./reducers/productReducers";
import { cartReducer } from "./reducers/cartReducers";
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
});
const cartItemsFromStorage = localStorage.getItem("cartItems")
? JSON.parse(localStorage.getItem("cartItems"))
: [];
const initialState = {
cart: { cartItems: cartItemsFromStorage },
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
Thanks in advance for any advice.

React redux prop object property undefined

I am new to React Redux. I am not sure what is wrong on my code. There is no error on the terminal but when I take a look on the browser there is a TypeError. ItemsProduct was on the props. I was wondering why it returns an error when I am trying to access the properties.
productDescription.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import axios from "axios";
import {
fetchProductsRequests,
fetchProductsSuccess,
fetchProductError,
} from "../../actions/productActions";
class ProductDescription extends Component {
componentDidMount() {
this.props.fetchProducts();
}
render() {
return (
<>
<div className="grid grid-cols-3 gap-6 mb-10">
<div className="col-start-2 col-end-4">
<h4>{this.props.itemsProduct[0].name}</h4>
</div>
</div>
</>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
itemsProduct: state.rootProduct.products.filter(
(prod) => prod.id == ownProps.match.params.id
),
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchProducts: () => {
dispatch(fetchProductsRequests());
axios
.get("http://localhost:3000/js/products.json")
.then((response) => {
dispatch(fetchProductsSuccess(response.data));
})
.catch((error) => {
dispatch(fetchProductError(error.message));
});
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ProductDescription);
productActions.js
export const FETCH_PRODUCTS_REQUESTS = "FETCH_PRODUCTS_REQUESTS";
export const FETCH_PRODUCTS_SUCCESS = "FETCH_PRODUCTS_SUCCESS";
export const FETCH_PRODUCTS_ERROR = "FETCH_PRODUCTS_ERROR";
export const fetchProductsRequests = () => {
return {
type: FETCH_PRODUCTS_REQUESTS,
};
};
export const fetchProductsSuccess = (product) => {
return {
type: FETCH_PRODUCTS_SUCCESS,
payload: product,
};
};
export const fetchProductError = (error) => {
return {
type: FETCH_PRODUCTS_ERROR,
payload: error,
};
};
productReducer.js
const initialState = {
loading: true,
products: [],
error: "",
};
const productReducer = (state = initialState, action) => {
switch (action.type) {
case "FETCH_PRODUCTS_REQUESTS":
return {
...state,
loading: true,
};
case "FETCH_PRODUCTS_SUCCESS":
return {
loading: false,
products: action.payload,
error: "",
};
case "FETCH_PRODUCTS_ERROR":
return {
loading: false,
products: [],
error: action.payload,
};
default:
return state;
}
};
export default productReducer;
Root Reducer
import { combineReducers } from "redux";
import productReducer from "./productReducer";
const rootReducer = combineReducers({
rootProduct: productReducer,
});
export default rootReducer;
You can do a quick check if there is data coming from your axios by doing this (it will prevent any undefined or null values)
dispatch(fetchProductsSuccess(response.data || 'no data'));
Also you should return your state in the reducer as follows:
case "FETCH_PRODUCTS_SUCCESS":
return {
...state,
loading: false,
products: action.payload,
error: "",
};
case "FETCH_PRODUCTS_ERROR":
return {
...state,
loading: false,
products: [],
error: action.payload,
};
Your
itemsProduct: state.rootProduct.products.filter(
(prod) => prod.id == ownProps.match.params.id
),
may return an empty array meaning you will not be able to retrieve that object in your view
<h4>{this.props.itemsProduct[0].name}</h4>

Redux action to reset - states are equal

I have the following initialState For React Redux:
const inistialStateRedux = {
configuredFilters: {
data: {
countries: [],
divisions: [],
companies: [],
locations: [],
fields: [],
search: '',
},
},
};
Now I want to create a RESET reducer.
It looks like that:
export const createReducer = (initialState, handlers) => (
state = initialState,
action
) => {
if (action.type in handlers) {
return handlers[action.type](state, action);
}
return state;
};
export const multiUse = (reducer, name = '') => (state = null, action) => {
if (action.name !== name) return state;
return reducer(state, action);
};
import {
createReducer
} from '../helper';
import * as Action from './actions';
import inistialStateRedux from '../inistialStateRedux';
export default createReducer({
data: {},
}, {
[Action.RESET_CONFIGURED_FILTERS]: (state) => ({
...state,
data: {
...inistialStateRedux.configuredFilters.data,
},
}),
});
But Redux Devtools shows, that the states are equal. What am I doing wrong ?
In the Actions you can use a dispatcher to reset the Form.
Like this:
import axios from 'axios';
import { ADD} from '../modelType';
import { reset } from 'redux-form';
export const add= formValues => async dispatch => {
const res = await axios.post('/api/model/', { ...formValues });
dispatch({
type: ADD,
payload: res.data
});
dispatch(reset('yourUniqueFormName'));
};

React Redux State Object properties

I wonder why my state todos named todo not todos in the redux dev tools ..
From where that name came ?
There is no initial state .. i wonder..
I'm following Stephen Grider udemy course but with todos instead of streams as a revision
why i have to return it by state.todo not state.todos??
My github Repo
Jsson server db.json file ( api file )
{
"todos": [
{
"title": "lorem ipsum ",
"description": "lorem ipsum",
"id": 4
}
]
}
todoReducer.js
import _ from 'lodash';
import {
CREATE_TODO,
EDIT_TODO,
FETCH_TODO,
FETCH_TODOS,
DELETE_TODO
} from '../actions/types';
export default (state = {}, action) => {
switch (action.type) {
case FETCH_TODOS:
return { ...state, ..._.mapKeys(action.payload, 'id') };
case CREATE_TODO:
case FETCH_TODO:
case EDIT_TODO:
return { ...state, [action.payload.id]: action.payload };
case DELETE_TODO:
return _.omit(state, action.payload);
default:
return state;
}
};
actions/index.js
import todos from '../apis/todos';
import history from '../history';
import {
SIGN_IN,
SIGN_OUT,
CREATE_TODO,
EDIT_TODO,
FETCH_TODO,
FETCH_TODOS,
DELETE_TODO
} from './types';
export const signIn = userId => {
return { type: SIGN_IN, payload: userId };
};
export const signOut = () => {
return { type: SIGN_OUT };
};
export const fetchTodos = () => async dispatch => {
const response = await todos.get('/todos');
dispatch({ type: FETCH_TODOS, payload: response.data });
};
export const createTodo = formValues => async dispatch => {
const response = await todos.post('/todos', formValues);
dispatch({ type: CREATE_TODO, payload: response.data });
history.push('/');
};
https://github.com/HosMercury/todos/blob/master/src/reducers/index.js here you are passing the list as todo not as todos.
Here you can check console in sandbox
https://codesandbox.io/s/github/HosMercury/todos

Categories