React .props is undefined - reactjs

I have initial state for a component 'Posts'
inside reducer like this:
import {FETCH_POSTS, NEW_POST, ASSISTANT_MANAGER, BARTENDER, OPEN_MODAL} from '../actions/types'
const initialState = {
items: [],
item: {},
showComponent: false,
selectedJob: {}
}
reducers/index.js
import {combineReducers} from 'redux';
import postReducer from './postReducer'
export default combineReducers({
posts: postReducer,
})
/components/Posts
class Posts extends Component {
constructor(){
super();
this.state = {
selectedJob: ""
};
}
_onButtonClick(selected_post, e) {
this.setState({
selectedJob:selected_post,
showComponent:true
});
console.log('my current props', this.props)
}
componentWillMount(){
this.props.fetchPosts();
}
render() {
const keys = generateKey(new Date().getTime())
var dictionary = this.props.posts
const postItemsArr = Object.keys(dictionary).map(post=>dictionary[post])
const number = 0
const postItems = postItemsArr.map(
post=>(
<Jumbotron key={generateKey(post.positiontitle) + generateKey(post.businessId)} >
<div className="position">{post.positiontitle}</div>
<br></br>
<BusinessName businessnameType={post.businessname} />
<br></br>
<JobDescription jobDescription={post.description_sanitized} />
<br></br>
<Button onClick={this._onButtonClick.bind(this, post)}>DETAILS</Button>
</Jumbotron>
))
return (
<div>
<h1> Jobs Listings </h1>
{postItems }
{this.props.showComponent ? <ModalComponent selectedJob={this.state.selectedJob}/>: null}
</div>
);
}
}
Posts.propTypes = {
fetchPosts: PropTypes.func.isRequired,
posts: PropTypes.array.isRequired,
newPost:PropTypes.objects,
}
const mapStateToProps = state => (
{
posts: state.posts.items,
newPost: state.posts.item,
showComponent: state.posts.showComponent
});
export default connect(mapStateToProps, {fetchPosts})(Posts);
What I'm trying to do is _onButtonClick(selected_post, e){
I want to set the props here which sets showComponent to True.
}
When I use this.props.showComponent(true) it says .showComponent is not defined.
inside my actions file I have showComponent Action which should dispatch the props but doesn't as expected.
./actions/postActions
import {FETCH_POSTS, NEW_POST, OPEN_MODAL} from './types';
export const showComponent = () => dispatch => {
dispatch({
type:OPEN_MODAL,
payload:false
})
}
export const fetchPosts = () => dispatch => {
fetch('http://127.0.0.1:8000/?filters')
.then(res=>res.json())
.then(posts => dispatch({
type:FETCH_POSTS,
payload:posts.results,
}))
}
Overally, how would I dispatch an action to reducers without having to use export connect like how I did for fetchPosts

Related

react/ redux app : actions not dispatching to store

I am creating a react app with redux for state management, I am facing issues when trying to dispatch and action, action is showing in redux devtools but it's not storing data to redux store not sure why it's happening, very unusual
If anyone knows why this happens please do let me know
My component is below
import axios from "axios";
import React, { Component } from "react";
import { connect } from "react-redux";
import { SETDATA } from "./store";
class Hello extends Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
this.firstdispatch();
}
firstdispatch = () => {
axios.get("https://jsonplaceholder.typicode.com/users").then((r) => {
console.log("data fetched", r.data);
this.props.setdata(r.data);
});
};
render() {
return (
<div>
{" "}
fff
{/* <button onClick={this.props.setdata}>getdata</button>
<button onClick={this.props.removedata}>decriment</button> */}
{/* <button onClick={props.push}>push</button>
<button onClick={props.pop}>pop</button> */}
{console.log(this.props)}
{this.props.users &&
this.props.users.map((m, i) => (
<div key={i}>
{m.title} {` - - - -`} {m.email}
</div>
))}
</div>
);
}
}
const mapstatetoprops = (state) => {
return {
users: state.users.users || [],
};
};
const mapDispatchTopProps = (dispatch) => {
return {
setdata: (users) => {
dispatch({ type: SETDATA, users });
},
};
};
export default connect(mapstatetoprops, mapDispatchTopProps)(Hello);
Actions reducers and store is below
updated
import * as redux from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
export const SETDATA = "users";
export const DELETEDATA = "data/deletedata";
const initSst = {
users: [],
};
const users = (state = initSst, action) => {
switch (action.type) {
case SETDATA:
return { ...state, ...action.data };
case DELETEDATA:
return { data: null };
default:
return state;
}
};
const rootReducer = redux.combineReducers({
users,
});
const store = redux.createStore(
rootReducer,
composeWithDevTools(
redux.applyMiddleware(thunk)
// other store enhancers if any
)
);
export default store;
Just update "SETDATA" to SETDATA in the switch/case
case SETDATA:
return { ...state, ...action.data };
once I updated the initial state to empty array its working
redux, actions, store
import * as redux from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
export const SETDATA = "users";
export const DELETEDATA = "data/deletedata";
const users = (state = [], action) => {
switch (action.type) {
case SETDATA:
return [...action.payload];
default:
return state;
}
};
const rootReducer = redux.combineReducers({
users: users,
});
const store = redux.createStore(
rootReducer,
composeWithDevTools(
redux.applyMiddleware(thunk)
// other store enhancers if any
)
);
export default store;
component
import axios from "axios";
import React, { Component } from "react";
import { connect } from "react-redux";
import { SETDATA } from "./store";
class Hello extends Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
this.firstdispatch();
}
firstdispatch = async () => {
await axios.get("https://jsonplaceholder.typicode.com/users").then((r) => {
// console.log("data fetched", r.data);
this.props.setdata(r.data);
});
};
render() {
return (
<div>
fff {console.log(this.props.users, "fff")}
{(this.props.users || []).map((m, i) => (
<div key={i}>
{m.title} {m.email}
</div>
))}
</div>
);
}
}
const mapstatetoprops = (state) => {
return {
users: state.users,
};
};
const mapDispatchTopProps = (dispatch) => {
return {
setdata: (users) => {
dispatch({ type: SETDATA, payload: users });
},
};
};
export default connect(mapstatetoprops, mapDispatchTopProps)(Hello);

Props not displaying from fetch call

I am trying to display recipes and not sure if I have this setup correctly. I am pulling recipes from a rails api via get fetch request. At the moment nothing is displaying.
Here is my recipe container:
import React, { Component } from 'react'
import RecipeList from '../components/RecipeList'
import RecipeInput from '../components/RecipeInput'
import { connect } from 'react-redux'
import { postRecipes } from '../actions/postRecipes.js'
import { getRecipes } from '../actions/getRecipes'
class RecipeContainer extends Component{
constructor(props){
super(props)
}
componentDidMount(){
getRecipes()
}
render(){
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} />
</div>
)
}
}
const mapStateToProps = state =>({
recipes: state.recipes
})
const mapDispatchToProps = dispatch =>{
return{
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
// deleteRecipe: id => dispatch({type: 'Delete_Recipe', id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeContainer)
Here is my get request....notice that I am returning my Recipe component here.
export const getRecipes = () => {
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
dispatch({ type: 'START_FETCHING_RECIPES_REQUEST' });
fetch(RECIPES_URL)
.then(response =>{ return response.json()})
.then(recipes => dispatch({ type: 'Get_Recipes', recipes }));
};
}
This is where I am trying to render the Recipe component from the get request
import React, {Component} from 'react';
// import { getRecipes } from '../actions/getRecipes.js';
import Recipe from './Recipe.js'
class RecipeList extends Component {
// componentDidMount(){
// getRecipes()
// }
render() {
return (
<div>
{this.props.recipes.map(recipe => (<Recipe recipe={recipe} key={recipe.id} />))}
</div>
)
}
}
export default RecipeList;
Edit: Added reducer
switch(action.type){
case 'Add_Recipe':
const recipe = {
name: action.name,
ingredients: action.ingredients,
chef_name: action.chef_name,
origin: action.origin,
category: action.category
}
return{
...state,
recipes: [...state.recipes, recipe],
}
case 'START_FETCHING_RECIPES_REQUEST':
return {
...state,
recipes: [...state.recipes],
requesting: true
}
case 'Get_Recipes':
return {
...state, recipes: action.recipes,
requesting: false
}
default:
return state
}
}
How can I correct this to make it work?
Issue
You are not passing the recipes to the RecipeList component that were fetched and presumably stored in state, and fed back to the UI via RecipeContainer.
Solution
Pass the recipe state from RecipeContainer to RecipeList as a prop. and then render/map the recipes from props.
RecipeContainer
class RecipeContainer extends Component{
componentDidMount() {
getRecipes();
}
render() {
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} /> // <-- pass recipe state
</div>
)
}
}
const mapStateToProps = state => ({
recipes: state.recipes,
});
const mapDispatchToProps = dispatch => {
return {
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
}
};
RecipeList
class RecipeList extends Component {
render() {
const { recipes } = this.props;
return (
<div>
{recipes.map(recipe => (
<Recipe recipe={recipe} key={recipe.id} />
))}
</div>
);
}
}
The actual solution to this was I needed to have an explicit return in my mapStateToProp function.
Eg.
const mapStateToProp = state =>{
return {
recipes: state.recipes
}
}

React Firebase Cart Add Items Cart Reducer not working

currently working on adding the items to cart using react and redux but the add item does not work
I'm taking the items from my collections page and then passing the key to the product preview page
I'm using react-redux cartReducer the three files are
just can't figure out how to pass the fish products
product page
cart actions
cart reducer
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import firebase from '../../firebase/firebase';
import { connect } from 'react-redux';
import { addItem } from '../../redux/cart/cart-actions'
class FishPage extends Component {
constructor(props) {
super(props);
this.ref = firebase.firestore().collection('fishproducts');
this.unsubscribe = null;
this.state = {
fishproducts: []
};
}
componentDidMount() {
const ref = firebase.firestore().collection('fishproducts').doc(this.props.match.params.id);
ref.get().then((doc) => {
if (doc.exists) {
this.setState({
fishproducts: doc.data(),
key: doc.id,
isLoading: false
});
} else {
console.log("No such document!");
}
});
}
render() {
return (
<div >
<div>
<div>
<h4><Link to="/">back</Link></h4>
<h3>
{this.state.fishproducts.name}
</h3>
</div>
<div >
<dl>
<dt>Description:</dt>
<dd>{this.state.fishproducts.description}</dd>
<dt>Discount:</dt>
<dd>{this.state.fishproducts.discount}</dd>
<dt>Size:</dt>
<dd>{this.state.fishproducts.size}</dd>
<dt>Weight:</dt>
<dd>{this.state.fishproducts.weight}</dd>
<dt>Price:</dt>
<dd>{this.state.fishproducts.price}</dd>
<dt>Stock:</dt>
<dd>{this.state.fishproducts.stock}</dd>
</dl>
<button onClick={() => addItem(this.state.fishproducts)} >ADD TO CART</button>
</div>
</div>
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
addItem: item => dispatch(addItem(item))
})
export default connect(null, mapDispatchToProps)(FishPage);```
this is cart action page
```import CartActionTypes from './cart-types';
export const toggleCartHidden = () => ({
type:CartActionTypes.TOGGLE_CART_HIDDEN
});
export const addItem = item => ({
type: CartActionTypes.ADD_ITEM,
payload: item
})```
this is cart reducer
```import CartActionTypes from './cart-types';
const INITIAL_STATE = {
hidden: true,
cartItems: []
};
export const cartReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case CartActionTypes.TOGGLE_CART_HIDDEN:
return {
...state,
hidden: !state.hidden
};
case CartActionTypes.ADD_ITEM:
return {
...state,
//cartItems: addItem(state.cartItems, action.payload)
cartItems: [...state.cartItems,action.payload]
};
default:
return state;
}
}
export default cartReducer;```
cant figure out how to pass fishproducts
So concept of React is that you need to access Firebase with a function. For that you should use a functional component.
React allows Hooks to get access to your state without a constructor so that's all
and then you'll need to use dispatch.
import React, { useState, useEffect } from 'react';
import firebase from '../../firebase/firebase';
import { Link } from 'react-router-dom';
import { connect , useDispatch} from "react-redux";
import { addItem} from '../../redux/cart/cart-actions';
const FishPage = (props) => {
const [state, setState] = useState({
name: '',
… rest of the values
isLoading: true,
})
const { name, … rest of the values } = state;
useEffect(() => {
setState({ isLoading: true });
const ref = firebase.firestore().collection('fishproducts').doc(props.match.params.id);
ref.get().then((doc) => {
setState({
name: doc.data().name,
… rest of the values
isLoading: false,
});
})
}, [props.match.params.id])
const item = [];
const dispatch = useDispatch();
return (
<div >
<div>
//your body here
<button onClick={() => dispatch(addItem(item))} >ADD TO CART</button>
</div>
</div>
</div>
);
}
const mapDispatchToProps = dispatch => {
return{
addItem: (item) => dispatch(addItem(item))
}
}
export default connect(null, mapDispatchToProps)(FishPage)

How to pass an input to Redux store and map filtered data accordingly

I am having trouble passing an object from the Parent to Child component.
I have read various similar posts but none of the solutions did work.
Error message: TypeError: Cannot read property 'map' of undefined
Parent: (App.js)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchData } from '../actions/actions';
class App extends Component {
constructor(props) {
super(props);
this.state = {
filter: "",
data: [],
filteredData: []
};
}
componentDidMount() {
this.props.dispatch(fetchData());
};
render() {
const { filter, data } = this.state;
const lowercasedFilter = filter.toLowerCase();
const filteredData = data.filter(item => {
return Object.keys(item).some(key =>
item[key].toString().toLowerCase().includes(lowercasedFilter)
);
});
return (
<div>
<UserList filteredData={filteredData}/>
{console.log(this.props.myData.Brastlewark)}
</div>
);
}
}
const mapStateToProps = ({ things: { myData, isFetching } }) => ({
myData,
isFetching
});
export default connect(mapStateToProps)(App);
Child: (Userlist.js)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchData } from '../actions/actions';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../layout.css'
class UserList extends Component {
constructor(props) {
super(props);
this.state = {
filter: "",
data: [],
filteredData: {}
};
}
componentDidMount() {
this.props.dispatch(fetchData());
};
handleChange = event => {
this.setState({ filter: event.target.value });
this.setState((state, props) => ({
data: this.props.myData.Brastlewark
}))
};
render() {
const {filteredData} = this.props;
return (
<div className="container-fluid">
<div className="jumbotron">
<h2>
<input value={this.props.filter} onChange={this.handleChange} placeholder="please search" />
</h2>
{!this.props.isFetching && <div> You may start searching for Orcs in the vilage by typing into the search box above. </div>}
</div>
<div className="container">
<div className="row">
{filteredData.map(item => (
<div className="col-md-4 col-xs-6" key={item.id}>
<div className="card">
<img className="card-img-top img-fluid" src={item.thumbnail} alt={item.age}/>
<div className="card-block">
<h5 className="card-title">{item.professions.toString().split(', ')}</h5>
<p className="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<p className="card-text"><small className="text-muted">Last updated 1 sec ago</small></p>
</div>
</div>
</div>
))}
</div>
</div>
{console.log(this.props.myData.Brastlewark)}
</div>
);
}
}
const mapStateToProps = ({ things: { myData, isFetching } }) => ({
myData,
isFetching
});
export default connect(mapStateToProps)(UserList);
Action.js
export const REQUEST_DATA = 'REQUEST_DATA'; // action to represent waiting for response
export const GET_DATA_FIRST = 'GET_DATA_FIRST'; // action to represent receiving of data
export const requestData = () => ({ type: REQUEST_DATA });
export const getDataFirst = myData => ({ type: GET_DATA_FIRST, myData });
export const fetchData = () => dispatch => {
dispatch(requestData());
return getData().then(things => {
// simulated delay
setTimeout(() => {
return dispatch(getDataFirst(things))
}, 1000);
});
};
const getData = async () => {
const res = await fetch('https://raw.githubusercontent.com/rrafols/mobile_test/master/data.json');
return await res.json();
}
Reducer (reducer.js)
import { combineReducers } from 'redux';
import { GET_DATA_FIRST, REQUEST_DATA } from '../actions/actions';
const initialState = {
isFetching: false,
myData: []
};
const things = (state = initialState, action) => {
switch (action.type) {
case REQUEST_DATA:
return {
...state,
isFetching: true
};
case GET_DATA_FIRST:
return {
...state,
isFetching: false,
myData: action.myData
};
default:
return state;
}
};
const rootReducer = combineReducers({
things // this key can be called anything, 'things' is just an example
});
export default rootReducer;
Perhaps somebody could help me converting this part to redux or just the simplest way, getting filtedData defined by input text?
Thanks in advance
The problem is probably that the .map function is running before the data is passed. This error usually appears when you try to go through an undefined object.
Try this code:
<div className="row">
{filteredData && filteredData.map(item => (
// do stuff with your item
))}
</div>
Mentioning the name of a variable before actually doing something with it checks whether is is undefined/null or not. If it's not, it will go on and do what you have programmed it to do.

React-redux state with external json

I want to list posts in PostList.js component from JSON file
I use react-redux for state managment and redux-saga to get json file
My components are Post.js and PostList.js:
Post.js
const Post = ({ post }) => {
<li>
{post}
</li>
}
export default Post
PostList.js
class PostList extends React.Component {
componentDidMount() {
console.log('did mount');
this.props.fetchPosts();
}
render() {
return (
<div>
<ul>
{this.state.posts(post => (
<Post key={post.id} {...post} />
))}
</ul>
</div>
)
}
}
export default PostList
Reducer
export default (state = [], action) => {
switch (action.type) {
case "FETCH_POSTS":
return {
...state,
loading: true,
posts: []
}
case "FETCH_FAILD":
return {
...state,
loading: false,
posts: []
}
case "FETCH_SUCCESS":
return Object.assign({}, state, {
posts: action.posts
})
default:
return state;
}
}
Actions.js
export const fetchPosts = () => {
return {
type: 'FETCH_POSTS'
}
}
export const fetchSuccess = data => ({
type: "FETCH_SUCCESS",
posts: data
})
export const fetchFaild = () => {
return {
type: 'FETCH_FAILD'
}
}
GetPosts.js (Container)
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import PostList from '../components/PostList'
import { fetchPosts } from '../actions'
const mapStateToProps = state => ({ posts: state.posts });
const mapDispatchToProps = dispatch => bindActionCreators({fetchPosts}, dispatch);
const GetPosts = connect(
mapStateToProps,
mapDispatchToProps
)(PostList)
export default GetPosts
Saga.js
export function* fetchProducts() {
try {
console.log('saga')
const posts = yield call(api_fetchPost);
yield put({ type: "FETCH_SUCCESS", posts});
} catch (e) {
yield put({ type: "FETCH_FAILD", e});
return;
}
}
export function* watchFetchProducts() {
yield takeEvery("FETCH_POSTS", fetchProducts)
}
You are fetching posts from the state of your postlist component. Redux mapStateToProps map the redux state to connected component's props and not state
class PostList extends React.Component {
componentDidMount() {
console.log('did mount');
this.props.fetchPosts();
}
render() {
return (
<div>
<ul>
{this.props.posts && this.props.posts.map(post => {
return ( <Post key={post.id} {...post} /> );
})}
</ul>
</div>
)
}
}
export default PostList
Change this.state.posts to this.props.posts

Resources