Cannot use redux data in ComponentDidMount - javascript

I am making a react-redux site.
I am accessing data called from an api via redux.
I understand that ComponentDidMount will not wait for this data to be called so I was wondering on a better way to split this data within a parent component into arrays for children components (or if this method is a bad choice).
This is the component and will hopefully shed some light on what is going on.
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { fetchPosts, fetchItins } from "../../../actions/postActions";
import TravelAlerts from "./TravelAlerts//travelAlert";
import IntelligenceAlerts from "./IntelligenceAlerts/IntelligenceAlert";
import AllAlerts from "./AllAlerts/AllAlerts";
class Alerts extends Component {
state = {
showAllAlerts: true,
showAllIntelligenceAlerts: false,
showAllTravellers: false,
currentPage: 1,
alertsPerPage: 20,
travelAlerts: [],
intelligenceAlerts: [],
};
componentDidMount() {
this.props.fetchPosts();
console.log(this.props.posts);
for (var key in this.props.posts) {
if (this.props.posts.hasOwnProperty(key)) {
if (key === "travelAlerts") {
alert("travel ALerts Hit");
} else if (key === "intelligenceAlerts") {
alert("intelligenceAlertsHIts");
} else {
}
console.log(key + " -> " + this.props.posts[key]);
}
}
}
//navigation helper
DisableAlerts() {
this.setState({
showAllAlerts: false,
showAllIntelligenceAlerts: false,
showAllTravellers: false,
});
}
//pagination change page
handleClick(number) {
this.setState({
currentPage: number,
});
}
ToogleAlertType(name) {
this.DisableAlerts();
if (name === "All") {
this.setState({ showAllAlerts: true });
} else if (name === "Intelligence") {
this.setState({ showAllIntelligenceAlerts: true });
} else if (name === "Travellers") {
this.setState({ showAllTravellers: true });
} else {
this.setState({ showAllAlerts: true });
}
}
render() {
return (
<div>
<button
style={{ width: "30%" }}
onClick={() => this.ToogleAlertType("ALL")}
>
ALL{" "}
</button>
<button
style={{ width: "30%" }}
onClick={() => this.ToogleAlertType("Intelligence")}
>
Intelligence{" "}
</button>
<button
style={{ width: "30%" }}
onClick={() => this.ToogleAlertType("Travellers")}
>
Travellers
</button>
<br />
<hr />
<div>
{this.state.showAllAlerts ? (
<>{/* <AllAlerts alerts={this.props.posts} /> */}</>
) : (
<></>
)}
</div>
<>
{this.state.showAllTravellers ? (
<>
<></>
{/* <TravelAlerts alerts={this.props.posts} /> */}
</>
) : (
<></>
)}
</>
<>
{this.state.showAllIntelligenceAlerts ? (
<>{/* <IntelligenceAlerts alerts ={this.props.posts}/> */}</>
) : (
<></>
)}
</>
</div>
);
}
}
Alerts.propTypes = {
fetchPosts: PropTypes.func.isRequired,
posts: PropTypes.object.isRequired,
// newPost: PropTypes.object
};
const mapStateToProps = (state) => ({
posts: state.posts.items,
// newPost: state.posts.item
});
export default connect(mapStateToProps, { fetchPosts })(Alerts);
The component is mapped to redux and is working fine however the data being retrieved is within an object and I would like this to be two separate arrays which I could then pass down to the child components etc.
This is what I am currently trying to do in the component did mount just to see if it can find the keys.
componentDidMount() {
this.props.fetchPosts();
console.log(this.props.posts);
for (var key in this.props.posts) {
if (this.props.posts.hasOwnProperty(key)) {
if (key === "travelAlerts") {
alert("travel ALerts Hit");
} else if (key === "intelligenceAlerts") {
alert("intelligenceAlertsHIts");
} else {
}
console.log(key + " -> " + this.props.posts[key]);
}
}
}
However the data does not show when mounted(works in render method but I feel that is not a good place to put it).
Is this a good direction to head in or should I have split these into two separate arrays before they have even reached the component? If so should this be done so in the reducer or in the actions?
Really appreciate any help as I am new to redux.
EDIT
This is my fetch posts
export const fetchPosts = () => (dispatch) => {
fetch(
"the url im using"
)
.then((res) => res.json())
.then((posts) =>
dispatch({
type: FETCH_POSTS,
payload: posts,
})
);
};
My reducer
import { FETCH_POSTS, NEW_POST, FETCH_ITINS } from "../actions/types";
const initialState = {
items: {},
item: {},
itins: [],
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_POSTS:
return {
...state,
items: action.payload,
};
case NEW_POST:
return {
...state,
item: action.payload,
};
case FETCH_ITINS:
return {
...state,
itins: action.payload,
};
default:
return state;
}
}

Related

React Redux Fetching Data From Api is Null

I am using react and redux to fetch data from an api, but when I try to retrieve the data in my view, I get an error :
TypeError: catData is undefined
I am having a hard time retrieving data from my api in my view, despite the fact that everything is fine on the backened. Any advise on what im doing wrong/ recommendations on how to retrieve data from the reducer will be highly appreciated.
My view looks like this
import React, { useState, useEffect } from "react";
import { connect} from "react-redux";
// #material-ui/core components
import Loader from 'react-loader-spinner'
import { fetchCategories } from "../../../actions/data"
const mapStateToProps = state => {
return {
catData: state.categories
}
}
const mapDispatchToProps = dispatch => {
return {
fetchCategories: () => dispatch(fetchCategories())
}
}
function CategoriesSection({ catData,fetchCategories}) {
useEffect(() => {
fetchCategories();
}, []);
return catData.loading ? (
<div xs={12} sm={10} md={10} lg={10} style={{marginTop: 10}} >
<Loader
type="Puff"
color="red"
height={200}
width={200}
style={{ display: "flex",
justifyContent: "center",
alignItems: "center" }}
/>
</div>
)
: catData.error ? (
<h2> {catData.error} </h2>
): (
<div>
<div className={classes.title} justify="center">
<h2 className={classes.title}>Our Categories</h2>
</div>
<div>
{
catData && catData.cat
}
</div>
</div>
)
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoriesSection)
My reducer looks like this :
import {
CATEGORIES_FETCH_REQUEST,
CATEGORIES_SUCCESS,
CATEGORIES_FAIL
} from "../actions/types";
const initialState = {
loading: false,
categories: [],
businesses: [],
error: ''
}
export default function(state = initialState, action) {
switch(action.type) {
case CATEGORIES_FETCH_REQUEST:
return {
...state,
loading: true
};
case CATEGORIES_SUCCESS:
return {
...state,
loading: false,
categories: action.payload
};
case CATEGORIES_FAIL:
return {
loading: false,
categories: null,
error: action.payload
};
default:
return state;
}
}
And my actions looks like this :
import axios from 'axios';
import {
CATEGORIES_SUCCESS,
CATEGORIES_FAIL,
CATEGORIES_FETCH_REQUEST,
} from "./types";
export const fetchCategories = () => {
return (dispatch) => {
dispatch(fetchCategoryRequest)
axios.get('https://api.xxxxxxx.com/api/v1/categories/')
.then(response => {
const categories = response.data
dispatch(fetchCategorySuccess(categories))
})
.catch(error => {
const err = error.message
dispatch(fetchCategoryFailure(err))
})
}
}
const fetchCategoryRequest = () => {
return {
type: CATEGORIES_FETCH_REQUEST
}
}
const fetchCategorySuccess = categories => {
return {
type: CATEGORIES_SUCCESS,
payload: categories
}
}
const fetchCategoryFailure = err => {
return {
type: CATEGORIES_FAIL,
payload: err
}
}

How can I write callback func for setState on event click

after onclick event occurs in backpackList.js, fetch data in context.js and then through setState I want to update noneUserCart . After that i want to get data from context.js to backpackList.js to show web page. but the data is inital data []. How can I solve this problem?!
I think this is a Asynchronous problem, but I'm new react, so I don't know how to write code for this. or do I use async, await.
Help me please!
import React, { Component } from 'react';
const ProductContext = React.createContext();
const ProductConsumer = ProductContext.Consumer;
class ProductProvider extends Component {
constructor() {
super();
this.state = {
totalProducts: 0,
isLogin: false,
cartList: [],
isNavOpen: false,
isCartOpen: false,
noneUserCart: [],
};
}
noneUserAddCart = bagId => {
fetch('/data/getdata.json', {
method: 'GET',
})
.then(res => res.json())
.catch(err => console.log(err))
.then(data => {
this.setState(
{
noneUserCart: [...this.state.noneUserCart, data],
},
() => console.log(this.state.noneUserCart)
);
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleCart: this.handleCart,
getToken: this.getToken,
addNoneUserCart: this.addNoneUserCart,
hanldeCheckout: this.hanldeCheckout,
openNav: this.openNav,
showCart: this.showCart,
habdleCartLsit: this.habdleCartLsit,
deleteCart: this.deleteCart,
noneUserAddCart: this.noneUserAddCart,
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
export { ProductProvider, ProductConsumer };
import React, { Component } from 'react';
import { ProductConsumer } from '../../context';
export default class BackpackList extends Component {
render() {
const {
backpackdata,
backdescdata,
isdescOpen,
showDesc,
descClose,
rangenumone,
rangenumtwo,
} = this.props;
return (
<div>
{backdescdata.map((bag, inx) => {
return (
<>
{isdescOpen && bag.id > rangenumone && bag.id < rangenumtwo && (
<div className="listDescContainer" key={inx}>
<div className="listDescBox">
<ProductConsumer>
{value => (
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
</div>
)}
</ProductConsumer>
<span className="descClosebtn" onClick={descClose}>
X
</span>
</div>
</div>
</div>
)}
</>
);
})}
</div>
);
}
}
fetch is asynchronous, this.setState is yet called when console.log
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
{value.noneUserCart}
{/* when finished, result should show here */}
</div>

Unable to handle state 'loading' properly in react with redux

Hey guys just moved to redux so in react what i was doing was in componentDidMount(), i was calling api and soon as i received the data i was setting loading to false (initially loading was true) to get rid of the 'react spinner',
but after using redux now in componentDidMount() i am calling my action creater which is in another and there i am receving my data so how do i manage 'loading' here ? can i somehow pass something from action creater to my component that triggers state and set loading to false ? or is there any other to do it ? How do you all manage it ?
here is my code
Home.js
class home extends Component {
UNSAFE_componentWillMount() {
this.props.verifyToken();
}
componentDidMount() {
this.props.categoryAction();
}
constructor(props) {
super(props);
this.state = {
categoriesWithTheirImages: [],
displayToggle: false,
loading: false,
};
}
renderCategory = () => {
return this.props.allCategories.map((item) => {
return (
<div
className="category_div"
key={item._id}
onClick={() => this.setState({ displayToggle: true })}
>
<img
src={item.image}
alt="miss-mistake"
className="category_image_home"
/>
<span className="category_heading_home">{item.categoryName}</span>
</div>
);
});
};
render() {
if (this.state.loading) {
return (
<div className="sweet-loading-main">
<FadeLoader
css={override}
sizeUnit={"px"}
size={50}
color={"#ff9d72"}
loading={this.state.loading}
/>
</div>
);
} else {
console.log(this.props.allCategories);
return (
<React.Fragment>
{/* <Fade left> */}
<Header />
<div className="main_content_homepage">
<p className="category_select">Please select a category</p>
<div className="category_list">{this.renderCategory()}</div>
</div>
{this.renderStoryActionDialog()}
{/* </Fade> */}
</React.Fragment>
);
}
}
}
const mapStateToProps = (state) => {
console.log(state);
const images = [family, ring, beer, feedback, academic];
let categoriesWithImages = state.getCategoryReducer.map((item, index) => {
item.image = images[index];
return item;
});
console.log(categoriesWithImages);
return { allCategories: categoriesWithImages };
};
export default connect(mapStateToProps, { verifyToken, categoryAction })(home);
and my action.js file
import { CATEGORY } from "../actionTypes";
export const categoryAction = ()=> {
return dispatch => {
fetch("http://localhost:3000/api/get_categories", {
method: "GET",
}).then(res=>res.json())
.then(response => {
console.log(response)
dispatch({ type: CATEGORY, payload: response });
})
.catch(err => console.log("Eror in adding", err));
};
};
reducer file
import { USER, CATEGORY} from "../actionTypes";
const getCategoryReducer = (state = [], action) => {
switch (action.type) {
case CATEGORY:
return action.payload;
default:
return state;
}
};
export default getCategoryReducer;
You should handle the loading state in your reducer file. At the moment, it's defined in your Component file. For e.g when you dispatch the action, it should update your loading state too. I would do something like this in reducer.
import { USER, FETCH_CATEGORY, FETCH_CATEGORY_SUCCESS, FETCH_CATEGORY_FAIL} from "../actionTypes";
const INITIAL_STATE = {
loading: false,
err: false,
data: []
}
const getCategoryReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_CATEGORY:
return Object.assign({}, state, {
loading: true,
data: [],
})
case FETCH_CATEGORY_SUCCESS
return Object.assign({}, state, {
loading: false,
data: action.payload,
})
case FETCH_CATEGORY_FAIL
return Object.assign({}, state, {
loading: false,
data: action.payload,
err: true
})
default:
return state;
}
};
export default getCategoryReducer;
and your action file would look something like this
import { FETCH_CATEGORY, FETCH_CATEGORY_SUCCESS, FETCH_CATEGORY_FAIL } from "../actionTypes";
export const categoryAction = ()=> {
//setting loading to true
return dispatch => {
dispatch({ type: FETCH_CATEGORY });
fetch("http://localhost:3000/api/get_categories", {
method: "GET",
}).then(res=>res.json())
.then(response => {
//setting loading to false
dispatch({ type: FETCH_CATEGORY_SUCCESS, payload: response });
})
.catch(err => console.log("Eror in adding", err); dispatch({ type: FETCH_CATEGORY_FAIL, payload: err }););
};
};
You can then read the loading props in your Home.js

How to Update the state in react redux-saga

I am newbie to react, redux-saga, I have a dropdown in page Display, when I select it move to respective component (eg. policy, score), In Component Pages, I have a button Add New, on clicking it will navigate to a page as mentioned in link url , which is a page having cancel button, on cancelling it returns to the Display.js but no dropdown selected,
I would like to keep the state articleMode, when navigating to a page and returning back to same page,
articleMode returns to state -1 instead of selected component Policy or Score
actions.js
export const updateArticleMode = data => {
console.log(data.body);
return {
type: CONSTANTS.UPDATE_ARTICLE_MODE,
data: data.body
};
};
queryReducer.js
import * as CONSTANTS from "../constants/constants";
const initialState = {
articleMode: ""
}
case CONSTANTS.UPDATE_ARTICLE_MODE: {
return {
...state,
articleMode: data.mode
};
}
export default queryReducer;
constants.js
export const UPDATE_ARTICLE_MODE = "UPDATE_ARTICLE_MODE";
Display.js
import React from "react";
import { connect } from "react-redux";
import Policy from "../policy";
import Score from "./../score";
import { updateArticleMode } from "../../../../actions/actions";
const articleMode = [
{ name: "Select", id: "-1" },
{ name: "Score", id: "Score" },
{ name: "Policy", id: "Policy" }
]
class Display extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
articleMode: "-1"
};
}
componentWillMount = () => {
this.setState({ articleMode: this.props.queryData.articleMode || "-1" });
};
_getComponent = () => {
const { articleMode } = this.state;
if (articleMode === "Policy") {
return <DisplayPolicy></DisplayPolicy>;
}
if (articleMode === "Score") {
return <DisplayScore></DisplayScore>;
}
}
render() {
return (
<React.Fragment>
<select name="example"
className="simpleSearchSelect1"
value={this.state.articleMode}
onChange={event => {
this.setState({ articleMode: event.target.value });
this.props.dispatch(
updateArticleMode({ body: { mode: event.target.value } })
);
}}
style={{ marginLeft: "2px" }}
>
{articleMode.length != 0 &&
articleMode.map((option, index) => {
const { name, id } = option;
return (
<option key={index} value={id}>
{name}
</option>
);
})}
</select>
{this.state.articleMode === "-1"
? this._renderNoData()
: this._getComponent()}
</React.Fragment>
)}
const mapStateToProps = state => {
return {
queryData: state.queryData
};
};
export default connect(mapStateToProps)(Display);
}
DisplayPolicy.js
import React from "react";
class DisplayPrivacyPolicy extends React.Component {
constructor(props) {
super(props);
}<Link
to={{
pathname: "/ui/privacy-policy/addNew",
state: {
language: "en"
}
}}
>
<button className="page-header-btn icon_btn display-inline">
<img
title="edit"
className="tableImage"
src={`${process.env.PUBLIC_URL}/assets/icons/ic_addstore.svg`}
/>
{`Add New`}
</button>
</Link>
AddNew.js
<Link
to =pathname: "/ui/display",
className="btn btn-themes btn-rounded btn-sec link-sec-btn"
>
Cancel
</Link>

Constantly getting undefined when iterating through an array

So I'm just practising my react redux skills. Im a beginner. I have created an action, reducer and several components. I'm basically making a todo app. I am fetching the data from an API which I have successfully done but the problem is arising when I'm trying to loop through the data and have it surrounded with <li> tags. I am getting Cannot read property 'map' of undefined which i dont understand why because in the console i can clearly see the array.
Action:
export function getLists(){
return function (dispatch) {
return fetch ("https://reqres.in/api/users?page=1")
.then( response => response.json())
.then (json => {console.log('sdsd'); console.log(json);
dispatch({ type: "ADD_ITEMS", payload: { json, loading: false} });
});
}
}
Reducer:
const initialState = {
todothings: [],
loading: true
};
function rootReducer (state = initialState, action) {
if( action.type === "ADD_ITEMS"){
console.dir("In the ADD_ITEMS reducer" + action.payload);
return Object.assign({}, state, {
todothings: state.todothings.concat(action.payload.json),
loading: action.payload.loading,
});
} else if ( action.type === "DELETE_ITEM") {
} else {
return state;
}
}
export default rootReducer;
ToDo Components:
import React, { Component } from 'react';
import { getLists } from '../actions/index';
import { connect } from 'react-redux';
import TodoItems from './TodoItems';
class TodosComponent extends Component {
componentDidMount(){
console.log('in the Todos comp -> componentDidMount()');
this.props.getList();
}
render(){
const {todothings , loading} = this.props;
if(!loading){
return(
<div>
<p>dfd</p>
{console.log(todothings)}
<TodoItems list={todothings}></TodoItems>
</div>
)
} else {
return (
<p>Fetching from upstream.</p>
)
}
}
}
function mapStateToProps(state){
return {
todothings: state.todothings,
loading: state.loading,
}
}
function mapDispatchToProps(dispatch){
return {
getList: () => dispatch(getLists())
};
}
const Todos = connect(mapStateToProps, mapDispatchToProps)(TodosComponent)
export default Todos;
TodoItem Component:
import React from 'react';
function TodoItems (props) {
return(
<div>
<ul>
{console.log('In the todoitems')}
{console.log(props)}
{props.list.map( element => (
<p>{element.data}</p>
))}
</ul>
</div>
);
}
export default TodoItems;
EDIT:
This is what I have so far now:
ToDoItems:
import React from 'react';
const TodoItems = ({ list = [] }) => {
if (list.length === 0) return null;
return (
<ul>
{list.map(item => (
<li key={item.id} {...item}>
<p>{item.first_name}</p>
</li>
))}
</ul>
);
};
export default TodoItems;
ToDo Component:
import React, { Component } from 'react';
import { getLists } from '../actions/index';
import { connect } from 'react-redux';
import TodoItems from './TodoItems';
class TodosComponent extends Component {
componentDidMount(){
console.log('in the Todos comp -> componentDidMount()');
this.props.getList();
}
render(){
const {todothings , loading} = this.props;
if(!loading){
return(
<div>
<p>dfd</p>
{console.log('in the todo comp')}
{console.log(todothings)}
<TodoItems list={todothings.data}></TodoItems>
</div>
)
} else {
return (
<p>Fetching from upstream.</p>
)
}
}
}
function mapStateToProps(state){
return {
todothings: state.todothings,
loading: state.loading,
}
}
function mapDispatchToProps(dispatch){
return {
getList: () => dispatch(getLists())
};
}
const Todos = connect(mapStateToProps, mapDispatchToProps)(TodosComponent)
export default Todos;
Reducer:
const initialState = {
todothings: [],
loading: true
};
function rootReducer (state = initialState, action) {
if( action.type === "ADD_ITEMS"){
console.dir("In the ADD_ITEMS reducer" + action.payload);
return Object.assign({}, state, {
todothings: state.todothings.concat(action.payload.json.data),
loading: action.payload.loading,
});
} else if ( action.type === "DELETE_ITEM") {
} else {
return state;
}
}
export default rootReducer;
Action:
export function getLists(){
return function (dispatch) {
return fetch ("https://reqres.in/api/users?page=1")
.then( response => response.json())
.then (json => {console.log('sdsd'); console.log(json);
dispatch({ type: "ADD_ITEMS", payload: { json, loading: false}
});
});
}
}
There are now no errors yet nothing is getting displayed:
(3) [{…}, {…}, {…}]
0: {id: 1, email: "george.bluth#reqres.in", first_name: "George", last_name: "Bluth", avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"}
1: {id: 2, email: "janet.weaver#reqres.in", first_name: "Janet", last_name: "Weaver", avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"}
2: {id: 3, email: "emma.wong#reqres.in", first_name: "Emma", last_name: "Wong", avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"}
length: 3
__proto__: Array(0)
Afternoon,
I think the data you want is from prop.list.data not props.list within <TodoItems />
In your component <TodDo /> try:
<TodoItems list={todothings.data}></TodoItems>
Then tidy up your stateless component <TodoItems /> (to cover an empty array):
const TodoItems = ({ list = [] }) => {
if (list.length === 0) return null;
return (
<ul>
{list.map(item => (
<li key={item.id} {...item}>
<ul>
{Object.keys(item).map((data, key) => (
<li {...{ key }}>{data}</li>
))}
</ul>
</li>
))}
</ul>
);
};
Note:
Your array has an object, with this shape:
{
id: 1,
email: "g.buth#reqres.in",
first_name: "George",
last_name: "Bluth"
}
What do you want to return in your <li>?
Update to loop through object.

Categories