Error: cannot read properties of undefined in react-redux - reactjs

I am trying to loop through every movie inside the movies array and render it.
There are 4 sections Trending,NewDisney,Originals,Recommended.
The movies' data is stored inside the firestore and I am retrieving it from there.
Here's the code
(All the styling is removed to make the code short here)
movieSlice.js file
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
recommend: null,
newDisney: null,
original: null,
trending: null,
};
const movieSlice = createSlice({
name: "movie",
initialState,
reducers: {
setMovies: (state, action) => {
state.recommend = action.payload.recommend;
state.newDisney = action.payload.newDisney;
state.original = action.payload.original;
state.trending = action.payload.trending;
},
},
});
export const { setMovies } = movieSlice.actions;
export const selectRecommend = (state) => state.movie.recommend;
export const selectNewdisney = (state) => state.movie.newDisney;
export const selectOriginal = (state) => state.movie.original;
export const selectTrending = (state) => state.movie.trending;
export default movieSlice.reducer;
Recommends.js file
import React from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import p1 from "../images/sample_img.jpg";
import { selectRecommend } from "../features/movies/movieSlice";
import { useSelector } from "react-redux";
const Recommend = () => {
const movies = useSelector(selectRecommend);
return (
<Container>
Recommended movies for you!
<Content>
{movies &&
movies.map((movie, key) => {
<Link to={"/detail/" + movie.id} key={key}>
<Wrap imgUrl={movie.cardImg} alt={movie.title} />
</Link>;
})}
</Content>
</Container>
);
};
export default Recommend;
Home.js file (Where all the components are imported)
import React, { useEffect } from "react";
import styled from "styled-components";
import ImgSlider from "./ImgSlider";
import NewDisney from "./NewDisney";
import Originals from "./Originals";
import Recommend from "./Recommends";
import Trending from "./Trending";
import Viewers from "./Viewers";
import { setMovies } from "../features/movies/movieSlice";
import { useDispatch, useSelector } from "react-redux";
import db from "../firebase";
import { selectUserName } from "../features/user/userSlice";
import { collection, query, onSnapshot } from "firebase/firestore";
const Home = () => {
const dispatch = useDispatch();
const userName = useSelector(selectUserName);
useEffect(() => {
let recommends = [];
let newDisneys = [];
let trendings = [];
let originals = [];
console.log("Hello!");
const q = query(collection(db, "movies"));
onSnapshot(q, (snapshot) => {
snapshot.docs.map((doc) => {
console.log(doc);
switch (doc.data().type) {
case "recommend":
recommends = [...recommends, { id: doc.id, ...doc.data() }];
break;
case "new":
newDisneys = [...newDisneys, { id: doc.id, ...doc.data() }];
break;
case "trending":
trendings = [...trendings, { id: doc.id, ...doc.data() }];
break;
case "originals":
originals = [...originals, { id: doc.id, ...doc.data() }];
break;
}
});
dispatch(
setMovies({
recommend: recommends,
trending: trendings,
newDisney: newDisneys,
original: originals,
})
);
});
}, [userName]);
return (
<Container>
<ImgSlider />
<Viewers />
<Recommend />
<NewDisney />
<Originals />
<Trending />
</Container>
);
};
export default Home;
But unfortunately this is not working and I am getting this error for all components (Recommend,Trending,NewDisney,Originals)
movieSlice.js:26 Uncaught TypeError: Cannot read properties of undefined (reading 'recommend')
at selectRecommend (movieSlice.js:26:1)
at memoizedSelector (with-selector.development.js:79:1)
at getSnapshotWithSelector (with-selector.development.js:134:1)
at mountSyncExternalStore (react-dom.development.js:16799:1)
at Object.useSyncExternalStore (react-dom.development.js:17727:1)
at useSyncExternalStore (react.development.js:1676:1)
at useSyncExternalStoreWithSelector (with-selector.development.js:145:1)
at useSelector (useSelector.js:41:1)
at Recommend (Recommends.js:8:1)
at renderWithHooks (react-dom.development.js:16305:1)
Please help me.

The error is occurring where you are doing this
export const selectRecommend = (state) => state.movie.recommend;
export const selectNewdisney = (state) => state.movie.newDisney;
export const selectOriginal = (state) => state.movie.original;
export const selectTrending = (state) => state.movie.trending;
Remove these and use this way to get values in App.js
const { recommend, newDisney, original, trending } = useSelector((state) => state.movie);

Related

React-Redux Search/Filter component countries API

I have problem with initialization of search and filter for ma countries api. I have stuck with problem how to implement code. I will be gratefull for any help or some advices how to do this. My files look like this:
Store.js
import { applyMiddleware, combineReducers, createStore, compose } from 'redux'
import getCountries from './getCountries'
const initialState = {
variable: {
data: [],
},
filters: {
searchPhrase: '',
continent: ''
}
}
const reducers = {
variable: getCountries, // variablesReducer
}
const storeReducer = combineReducers(reducers)
const store = createStore(
storeReducer,
initialState,
compose(applyMiddleware(), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) // przed deployem usunac
)
export default store
SearchPhraseRedux.js
//SearchEngine
export const getData = ({variable}, filters) => {
if(!filters.searchPhrase){
return variable.data
}
//Logic
//export const getCountryByName = ({variable}, name) =>
//variable.data.find(e=>e.name.common === name)
}
const reducerName = 'variable'
const createActionName = (name) => `app/${reducerName}/${name}`
export const SET_DATA = createActionName("SET_DATA")
export const setData = payload => ({payload, type: SET_DATA})
export default function reducer(statePart=[], action={}){
switch(action.type) {
case SET_DATA:
return {...statePart, data: action.payload}
default:
return statePart
}
}
countryContainer.js
import { connect } from "react-redux"
import { getData, setData} from '../../redux/getCountries'
import Countries from "./countries"
const mapStateToProps = (state) => ({
data: getData(state),
})
const mapDispatchToProps = dispatch =>({
setData: (value) => dispatch(setData(value)),
})
export default connect(mapStateToProps, mapDispatchToProps)(Countries)
countries.js
import React from 'react'
import { useEffect} from 'react'
import Axios from 'axios'
import '../../../src/index.css'
import {Link} from 'react-router-dom'
const Countries = ({data,setData}) => {
const url = 'https://restcountries.com/v3.1/all'
useEffect(() => {
Axios.get(url)
.then((rest) =>{
setData(rest.data)
})
},[url, setData])
return (
<div>
<section className='grid'>
{data.map((country) => {
const {population, region, capital} = country
const {png} = country.flags
const {official, common} = country.name
return <article key={common}>
<div>
<Link classsName='link' to={`/countries/${common}`}>
<img src ={png} alt={official}/>
</Link>
<div className='details'>
<h3>{common}</h3>
<h4>Population: <span>{population}</span></h4>
<h4>Region: <span>{region}</span></h4>
<h4>Capital: <span>{capital}</span></h4>
</div>
</div>
</article>
})}
</section>
</div>
)
}
export default Countries
I have tried to get One country like this:
export const getCountryByName = ({variable}, name) => variable.data.find(e=>e.name.common === name)
but,by extending this code, it is difficult for me to make search and filter

Map fetched data in React with Redux Toolkit

i´m trying to fetch all the product i have in my DB (mongodb) using the fetch API. The result i get i store in a slice using Redux Toolkit. The problem is when i pass the data fetched and stored to a component as a props, the result is not beeing displayed.
slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
products: [],
};
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setProducts(state, action) {
state.products.push(action.payload);
},
},
});
export const { setProducts } = productSlice.actions;
export default productSlice.reducer;
store
import { configureStore } from "#reduxjs/toolkit";
import uiSlice from "./ui-slice";
import userSlice from "./user-slice";
import productSlice from './product-slice';
const store = configureStore({
reducer: {
ui: uiSlice,
user: userSlice,
products: productSlice
},
});
export default store;
function i used to fetch the products
export const fetchProduct = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:xx/xxx");
const data = await response.json();
let loadedProducts = [];
for (const key in data) {
loadedProducts.push({
id: data[key]._id,
productName: data[key].productName,
price: data[key].price,
imageUrl: data[key].imageUrl,
});
}
dispatch(setProducts(loadedProducts));
} catch (error) {
console.log(error);
}
};
I get the value stored in my redux state with useSelector and use it to fetch the products
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Container } from "react-bootstrap";
import { fetchProduct } from "../../actions/productActions";
import Hero from "../hero/Hero";
import Footer from "../footer/Footer";
import DisplayProductsList from "../displayProduct/DisplayProductsList";
export default function Home() {
const productsInfo = useSelector((state) => state.products.products);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProduct());
}, []);
return (
<>
<Hero />
<DisplayProductsList products={productsInfo} />
<Container fluid>
<Footer></Footer>
</Container>
</>
);
}
And then i map it
export default function DisplayProductsList(props) {
console.log(props)
return (
<ul>
{props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))}
</ul>
);
}
console log the props i sent
if i console log the state in the selector this is what i get
code in the backend
module.exports.fetchProduct = async (req, res) => {
try {
const products = await Product.find({});
console.log(products)
if (products) {
res.json(products);
}
} catch (error) {
console.log(error);
}
};
As I can see in your code, you are storing an array of objects i.e. 'loadedProducts' into 'products' which is also an array. Something like this:
nested array
So in order to get the productsInfo in DisplayProductsList component, instead of doing this:
{props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))}
you should do something like this:
{props.products[0].map((product) => (
//rest of your code
))}

Redux - useSelector returns state as undefined

I'm following a tutorial on learning Redux and I'm stuck at this point where state that should have an image url is returned as undefined.
Image is successfully saved in firbase storage and dispatched but when I try to get the url on new route with useSelector it is undefined.
import React, {useEffect} from "react";
import {useSelector} from "react-redux";
import {useHistory} from "react-router-dom";
import "./ChatView.css";
import {selectSelectedImage} from "./features/appSlice";
function ChatView() {
const selectedImage = useSelector(selectSelectedImage);
const history = useHistory();
useEffect(() => {
if(!selectedImage) {
exit();
}
}, [selectedImage])
const exit = () => {
history.replace('/chats');
}
console.log(selectedImage)
return (
<div className="chatView">
<img src={selectedImage} onClick={exit} alt="" />
</div>
)
}
export default ChatView
reducer created for chat (slice):
import { createSlice } from '#reduxjs/toolkit';
export const appSlice = createSlice({
name: 'app',
initialState: {
user:null,
selectedImage:null,
},
reducers: {
login: (state, action) => {
state.user = action.payload;
},
logout: (state) => {
state.user = null;
},
selectImage:(state, action) => {
state.selectedImage = action.payload
},
resetImage:(state) => {
state.selectedImage = null
}
},
});
export const { login, logout, selectImage, resetImage} = appSlice.actions;
export const selectUser = (state) => state.app.user;
export const selectSelectedImage = (state) => state.app.selectImage;
export default appSlice.reducer;
and code for dispatching that imageURL which when i console.log it gives the correct url:
import {Avatar} from "#material-ui/core";
import StopRoundedIcon from "#material-ui/icons/StopRounded"
import "./Chat.css";
import ReactTimeago from "react-timeago";
import {selectImage} from "./features/appSlice";
import {useDispatch} from "react-redux";
import {db} from "./firebase";
import {useHistory} from "react-router-dom";
function Chat({id, username, timestamp, read, imageUrl, profilePic}) {
const dispatch = useDispatch();
const history = useHistory();
const open = () => {
if(!read) {
dispatch(selectImage(imageUrl));
db.collection('posts').doc(id).set({read:true,}, {merge:true});
history.push('/chats/view');
}
};
return (
<div onClick={open} className="chat">
<Avatar className="chat__avatar" src={profilePic} />
<div className="chat__info">
<h4>{username}</h4>
<p>Tap to view - <ReactTimeago date={new Date(timestamp?.toDate()).toUTCString()} /></p>
</div>
{!read && <StopRoundedIcon className="chat__readIcon" />}
</div>
)
}
export default Chat
Your selector is trying to access the wrong field.
export const selectSelectedImage = (state) => state.app.selectImage;
Should actually be:
export const selectSelectedImage = (state) => state.app.selectedImage;
as your state has selectedImage field and not selectImage.

dispatching action in redux-saga is not fetching data

I am studying redux-saga and I want to fetch data from :
https://jsonplaceholder.typicode.com/posts
and in my redux folder I have the fallowing:
(it can be checked in this github repository
https://github.com/jotasenator/redux-saga-fetching-example/tree/main/src)
\src\redux\api.js
import axios from 'axios'
export const loadPostApi = async () => {
await axios.get(`https://jsonplaceholder.typicode.com/posts`)
}
the get request to the address in question
src\redux\app.actions.js
export const loadPostStart = () => ({
type: 'LOAD_POST_START',
})
export const loadPostSuccess = (posts) => ({
type: 'LOAD_POST_SUCCESS',
payload: posts,
})
export const loadPostFail = (error) => ({
type: 'LOAD_POST_FAIL',
payload: error,
})
those are the actions functions
src\redux\app.reducer.js
const INITIAL_STATE = {
loading: false,
posts: [],
errors: null,
}
export const appReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'LOAD_POST_START':
return {
...state,
loading: true,
}
case 'LOAD_POST_SUCCESS':
return {
...state,
posts: action.payload,
loading: false,
}
case 'LOAD_POST_FAIL':
return {
...state,
errors: action.payload,
loading: false,
}
default:
return state;
}
}
the reducer of the fetching, updating state,
src\redux\counterReducer.js
import { types } from "./types";
const initialState = {
value: 0
}
export const counterReducer = (state = initialState, action) => {
switch (action.type) {
case types.adicionar:
return {
...state,
value: state.value + 1
}
case types.resetear:
return {
...state,
value: 0
}
case types.restar:
return {
...state,
value: state.value - 1
}
default:
return state
}
}
this is the reducer of the counter app, with different approach, types are isolated in another file
src\redux\rootReducer.js
import { combineReducers } from 'redux'
import { counterReducer } from './counterReducer'
import { appReducer } from './app.reducer'
export const rootReducer = combineReducers({
counterReducer,
appReducer
})
the rootReducer for gathering the reducers
src\redux\sagas.js
import { put, takeLatest, call } from 'redux-saga/effects'
import { loadPostApi } from './api'
import { loadPostFail, loadPostSuccess } from './app.actions'
export function* onLoadPostStartAsync() {
try {
const response = yield call(loadPostApi)
yield put(loadPostSuccess(response.data))
} catch (error) {
yield put(loadPostFail(error))
}
}
export function* onLoadPost() {
yield takeLatest('LOAD_POST_START', onLoadPostStartAsync)
}
export default function* rootSaga() {
yield ([
onLoadPost(),
])
}
saga onLoadPostStartAsync called by saga onLoadPost inside rootSaga
src\redux\store.js
import { applyMiddleware, compose, createStore } from "redux";
import createSagaMiddleware from 'redux-saga'
import { rootReducer } from "./rootReducer";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))
export const store = createStore(rootReducer, enhancer)
sagaMiddleware.run(rootSaga)
this is the store with the redux_devtool_extension, the reducers, and running rootSaga
src\redux\types.js
export const types = {
adicionar: 'ADICIONAR',
resetear: 'RESETEAR',
restar: 'RESTAR'
}
those are the types of the counterApp reducer
src\Counter.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
export const Counter = () => {
const dispatch = useDispatch()
const { value } = useSelector(state => state.counterReducer)
const handleAdicionar = () => {
dispatch({ type: 'ADICIONAR' })
}
const handleResetear = () => {
(value !== 0) && dispatch({ type: 'RESETEAR' })
}
const handleRestar = () => {
dispatch({ type: 'RESTAR' })
}
console.log(value)
return (
<div>
<button onClick={handleAdicionar}>Adicionar</button>
{' '}
<button onClick={handleResetear}>Resetear</button>
{' '}
<button onClick={handleRestar}>Restar</button>
<hr />
</div>
)
}
this is the Counter component, it works ok
src\Fetching.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { loadPostStart } from './redux/app.actions'
export const Fetching = () => {
const dispatch = useDispatch()
const fetchPost = () => {
dispatch(loadPostStart())
}
const state = useSelector(state => state.appReducer)
console.log(state)
return (
<>
<h1>Fetching from https://jsonplaceholder.typicode.com</h1>
<button onClick={fetchPost}>Fetching</button>
{
!state.loading && state.posts.map((post) => (
<li key={post.id}><h2>{post.title}</h2></li>
))
}
</>
)
}
the Fetching component click on the button calls fetchPost function who dispatch loadPostStart() function which is the same of dispatching {type: 'LOAD_POST_START'}, but nothing happens here when clicking, not fetch nothing from here https://jsonplaceholder.typicode.com/posts
src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { store } from './redux/store';
import { Provider } from "react-redux";
import { Unificator } from './Unificator';
ReactDOM.render(
<Provider store={store}>
<Unificator />
</Provider>,
document.getElementById('root')
);
component Unificator has Counter and Fetching component
src\Unificator.js
import React from 'react'
import { Counter } from './Counter'
import { Fetching } from './Fetching'
export const Unificator = () => {
return (
<div>
<Counter />
<Fetching />
</div>
)
}
as you can see is about of two reducers, one is the famous counter, and the another one is the fetching issue, do not know what is happening that is not fetching the data
obviously, i am doing something wrong here...don t see where
Axio returns promise, You need to capture that and return. Please try replacing below code.
export const loadPostApi = async () => {
await axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then((response) => {
console.log('Response', response);
return response;
})
.catch((error) => {
console.log('error', error);
})
}

How to pass object to Redux

I would like ask you about passing object to Redux.
Below is my code.
// src/actions/writingType.js
export const write = () => ({
type: 'WRITE',
})
export const update = (obj) => ({
type: 'UPDATE',
obj
})
// src/reducers/writingType.js
const initialState = {
writingType: "WRITE",
obj: null
}
const writingTypeReducer = (state = initialState, action) => {
console.log('\n inside writingTypeReducer');
console.log(action);
switch (action.type) {
case 'WRITE':
return {
...state,
writingType: 'WRITE'
};
case 'UPDATE':
return {
...state,
writingType: 'UPDATE',
obj: action.obj
};
default:
return state;
}
}
export default writingTypeReducer;
// Contentview.js
import React, { useContext } from 'react';
import { Route, Link } from 'react-router-dom';
import MarkdownRenderer from 'react-markdown-renderer';
import './Contentview.css';
import { connect } from 'react-redux'
import { write, update } from '../../actions/writingType'
import { UserConsumer } from '../../contexts/userContext';
import { Test } from '../../contexts/Test';
const Contentview = (props) => {
/*
category: "React"
contentObj:
contents: "something"
createdDatetime: "2019.10.26 08:52:05"
title: "something"
wikiIndex: 1
*/
console.log('\n Contentview');
console.log(props);
console.log('\n update(props.contentObj);');
update(props.contentObj);
const url = "/editor/" + props.category;
const updateUrl = "/update/" + props.category;
return (
<div>
<div className="categoryDiv">{props.category}</div>
<div className="writingDiv"><Link to={url}> A </Link></div>
<div className="updateDiv"><Link to={updateUrl} > B </Link></div>
<hr />
<MarkdownRenderer markdown={props.contentObj.contents} />
</div>
);
};
// export default Contentview;
const mapStateToProps = (state, props) => ({
writetypestate: state.writingType,
obj: props.contentObj
})
const mapDispatchToProps = dispatch => ({
write: () => dispatch(write()),
update: (obj) => {
console.log('Contentview, mapDispatchToProps, update');
dispatch(update(obj))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Contentview)
I used update(props.contentObj); in Contentview.js to pass props.contentObj to Redux and update obj of initialState in src/reducers/writingType.js. But obj of initialState hasn't changed and existed as null.
How should I change code?
Thank you.
use props.update to call in the main file
// Contentview.js
import React, { useContext } from 'react';
import { Route, Link } from 'react-router-dom';
import MarkdownRenderer from 'react-markdown-renderer';
import './Contentview.css';
import { connect } from 'react-redux'
import { write, update } from '../../actions/writingType'
import { UserConsumer } from '../../contexts/userContext';
import { Test } from '../../contexts/Test';
const Contentview = (props) => {
/*
category: "React"
contentObj:
contents: "something"
createdDatetime: "2019.10.26 08:52:05"
title: "something"
wikiIndex: 1
*/
console.log('\n Contentview');
console.log(props);
console.log('\n update(props.contentObj);');
props.update(props.contentObj);
const url = "/editor/" + props.category;
const updateUrl = "/update/" + props.category;
return (
<div>
<div className="categoryDiv">{props.category}</div>
<div className="writingDiv"><Link to={url}> A </Link></div>
<div className="updateDiv"><Link to={updateUrl} > B </Link></div>
<hr />
<MarkdownRenderer markdown={props.contentObj.contents} />
</div>
);
};
// export default Contentview;
const mapStateToProps = (state, props) => ({
writetypestate: state.writingType,
obj: props.contentObj
})
const mapDispatchToProps = dispatch => ({
write: () => dispatch(write()),
update: (obj) => {
console.log('Contentview, mapDispatchToProps, update');
dispatch(update(obj))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Contentview)
Please use the above code

Resources