For some reason, when i render InventoryItem component inside InventoryPage component, dispatch is returned as undefined, but when i render it inside any other component it works perfectly.
Here's InventoryItem:
// REDUX
import { connect } from 'react-redux';
import { addItem } from '../../../Redux/reducers/cart/actions/cartActions.js';
// REUSABLE COMPONENTS
import { Button } from '../button/button.jsx';
export const InventoryItem = ({ drilledProps, dispatch }) => {
const { name, price, imageUrl } = drilledProps;
return (
<div className="collection-item">
<div
className="image"
style={{
background: `url(${imageUrl})`
}}
/>
<div className="collection-footer">
<span className="name">{name}</span>
<span className="price">${price}</span>
</div>
<Button
handler={() => dispatch(addItem(drilledProps))}
modifier="_inverted"
type="button"
text="ADD TO CART"
/>
</div>
);
};
export default connect(null)(InventoryItem);
When i render it here, dispatch returns undefined:
// REDUX
import { connect } from 'react-redux';
import { categorySelector } from '../../../../Redux/reducers/inventory/selectors/inventorySelectors.js';
// COMPONENTS
import { InventoryItem } from '../../../reusable-components/inventory-item/inventory-item.jsx';
const InventoryPage = ({ reduxProps: { categoryProps } }) => {
const { title: category, items } = categoryProps;
return (
<div className="collection-page">
<h2 className="title">{category}</h2>
<div className="items">
{
items.map((item) => (
<InventoryItem key={item.id} drilledProps={item}/>
))
}
</div>
</div>
);
};
const mapStoreToProps = (currentStore, ownProps) => ({
reduxProps: {
categoryProps: categorySelector(ownProps.match.params.categoryId)(currentStore)
}
});
export default connect(mapStoreToProps)(InventoryPage);
When i render it here it works perfectly:
// COMPONENTS
import InventoryItem from '../../../../reusable-components/inventory-item/inventory-item.jsx';
export const InventoryPreview = ({ title, items }) => {
return (
<div className="collection-preview">
<h1 className="title">{title}</h1>
<div className="preview">
{items
.filter((item, index) => index < 4)
.map((item) => (
<InventoryItem key={item.id} drilledProps={item} />
))}
</div>
</div>
);
};
Thanks for the help in advance!
You are importing the unconnected component, the connected component exports as default so you should do:
//not import { InventoryItem }
import InventoryItem from '../../../../reusable-components/inventory-item/inventory-item.jsx';
when you do export const InventoryItem = then you import it as import {InventoryItem} from ... but when you import the default export: export default connect(... then you import it as import AnyNameYouWant from ...
Related
how to add an action when the add button is clicked then the item will display the product?
This code does not display the product when clicked
I've been fiddling with it but it's still an error, who knows, someone here can help me and fix the code and explain it
cartAction.js
import {ADD_TO_CART} from './ActionType';
export const addToCart = (items) =>{
return{
type: ADD_TO_CART,
items //I'm confused about what to do in the action payload here
}
}
cartReducer.js
import ImgT from './images/image-product-1-thumbnail.jpg';
import {ADD_TO_CART} from './ActionType';
const initState = {
items: [
{id:1,title:'title',
brand:cc',
images:ImgT,
desc:'helo world',
price:125.00}
],addItems:[],
total:0
}
const cartReducer= (state = initState,action)=>{
if(action.type === ADD_TO_CART){
return {...state, addItems:state.addItems} //I'm confused about what to do in the action payload here
}
return state
}
export default cartReducer;
cart.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Cart extends Component {
render() {
return (
<div>
{this.props.Items.map(item =>{
<div key={item.id}>
{/* <img src={item.images} /> */}
<p>{item.title}</p>
<h4>brand: {item.brand}</h4>
<p>{item.price}</p>
</div>
})
}
</div>
);
}
}
const mapToProps = (state ) =>{
return{
Items:state.addItems}
}
export default connect(mapToProps)(Cart);
Home.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Cart from './Cart';
import {addToCart} from './cartAction';
class Home extends Component{
handleClick = () =>{
this.props.addToCart()
}
render(){
let item = this.props.items.map(item =>{
return(
<div className='card' key={item}>
<img style={{width:'10rem'}} src={item.images}/>
<p className='card-title '>{item.title}</p>
<h2 className='card-title fs-4'>{item.brand}</h2>
<button onClick={() => this.handleClick()} className='btn btn-primary'>Add to cart</button>
<h3 className='fs-5'>{item.price}</h3>
</div>
)
})
return(
<div className="container">
<h3>Home</h3>
{item}
<Cart/>
</div>
)
}
}
const mapToProps = (state) => {
return{
items:state.items,
}
}
const mapDispatchToProps = (dispatch) => {
return{
addToCart: () => dispatch(addToCart())}
}
export default connect(mapToProps,mapDispatchToProps)(Home);
Two components talking to each other, when adding a product to the cart (component # 1), update the setState, via service (component # 2).
When adding the product to the cart, an error returns, saying that I don't have access to the export function of component # 1.
#1 Component
import React, { useContext, useEffect } from 'react';
import Link from 'next/link';
import { Clipboard } from 'react-feather';
import { OrderCartButton } from './styles';
import OrderService from '~/services/orderservice';
import Context from '~/utils/context';
function OrderCart() {
const { state, actions } = useContext(Context);
function updateQty() {
const qty = OrderService.count();
actions({ type: 'setState', payload: { ...state, value: qty } });
}
useEffect(() => {
updateQty();
}, []);
return (
<>
<Link href="/order">
<OrderCartButton data-order-qty={state.value}>
<Clipboard />
</OrderCartButton>
</Link>
</>
);
}
export default OrderCart;
#2 Component
import { reactLocalStorage } from 'reactjs-localstorage';
import { toast } from 'react-toastify';
class OrderService {
async add(product) {
const oldorder = reactLocalStorage.getObject('_order_meumenu');
if (oldorder.length) {
const merged = [...oldorder, ...product].reduce(
(r, { id, qty, title, description, price, image }) => {
const item = r.find((q) => q.id === id);
if (item) item.qty += qty;
else r.push({ id, qty, title, description, price, image });
return r;
},
[]
);
await reactLocalStorage.setObject('_order_meumenu', merged);
} else {
await reactLocalStorage.setObject('_order_meumenu', product);
}
toast.success('Produto adicionado ao Pedido');
const qty = await this.count();
return qty;
},
async count() {
const order = reactLocalStorage.getObject('_order_meumenu');
return order.length || 0;
},
}
export default OrderService;
Component #3 - Context Moved to callback
import React, { useState, useContext } from 'react';
import { Plus, Minus } from 'react-feather';
import { ProductContainer } from './styles';
import currency from '../../utils/currency';
import OrderService from '~/services/orderservice';
import Context from '~/utils/context';
function Product(product) {
const { state, actions } = useContext(Context);
const [qty, setQty] = useState(1);
function addProductOrder(elem, elemQty) {
// eslint-disable-next-line no-param-reassign
const newElem = [];
newElem.push({ ...elem, qty: elemQty });
OrderService.add(newElem).then((val) =>
actions({ type: 'setState', payload: { ...state, value: val } })
);
}
return (
<ProductContainer>
<div className="product-image">
<img
className="image"
src={product.image}
alt={product.title}
data-product-image
/>
</div>
<div className="product-details">
<div className="product-top">
<div className="product-title">
<span className="title" data-product-title>
{product.title}
</span>
<span className="desc" data-product-desc>
{product.description}
</span>
</div>
<button
type="button"
className="product-add"
onClick={() => addProductOrder(product, qty)}
>
<span className="btn -icon -rounded" title="Add Produto">
<Plus className="icon" />
</span>
</button>
</div>
<div className="product-bottom">
<div className="product-qty">
<div className="product-control-number">
<Minus className="icon" onClick={() => setQty(qty - 1)} />
<input
className="input"
type="number"
min="1"
max="9"
value={qty}
readOnly
data-number-value
/>
<Plus className="icon" onClick={() => setQty(qty + 1)} />
</div>
</div>
<div
className="product-price"
data-product-price={product.price}
data-product-totalprice="9"
>
{currency(product.price)}
</div>
</div>
</div>
</ProductContainer>
);
}
export default Product;
There are a few things to fix:
import OrderCart from '~/components/OrderCart';
// ...
OrderCart.updateQty();
the default export in ~/components/OrderCart is the class component(OrderCart) and updateQty is another function in the same file, so the import statement should be something like:
import { updateQty } from '~/components/OrderCart';
and the usage should be
updateQty()
but this will not work because calling a function that returns some object will not cause a rerender.
So, to fix this you should pass a callback to the child component that calls the add function, and call the callback after invoking add.
The callback function to pass as props to the child can be handleUpdateQty.
So, as the title suggests, Cards component is receiving props from UserPosts, as well as it's connected to the store to dispatch an action. But it looks like this is not working at all. Connecting a component is not working for me. Maybe I am missing something? Can someone show me the correct way to do it. I'm trying to delete a post on clicking on the delete button.
Here is the code.
UserPosts
import React, { Component } from "react"
import { getUserPosts, getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import Cards from "./Cards"
class UserFeed extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
if (authToken) {
this.props.dispatch(getCurrentUser(authToken))
if (this.props && this.props.userId) {
this.props.dispatch(getUserPosts(this.props.userId))
} else {
return null
}
}
}
render() {
const { isFetchingUserPosts, userPosts } = this.props
return isFetchingUserPosts ? (
<p>Fetching....</p>
) : (
<div>
{userPosts &&
userPosts.map(post => {
return <Cards key={post._id} post={post} />
})}
</div>
)
}
}
const mapStateToPros = state => {
return {
isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
userPosts: state.userPosts.userPosts,
userId: state.auth.user._id
}
}
export default connect(mapStateToPros)(UserFeed)
Cards
import React, { Component } from "react"
import { connect } from "react-redux"
import { deletePost } from "../actions/userActions"
class Cards extends Component {
handleDelete = postId => {
this.props.dispatch(deletePost(postId))
}
render() {
const { _id, title, description } = this.props.post
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{ border: "1px grey" }}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
<button className="button is-success">Edit</button>
<button
onClick={this.handleDelete(_id)}
className="button is-success"
>
Delete
</button>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = () => {
return {
nothing: "nothing"
}
}
export default connect(mapStateToProps)(Cards)
deletePost
export const deletePost = (id) => {
return async dispatch => {
dispatch({ type: "DELETING_POST_START" })
try {
const deletedPost = await axios.delete(`http://localhost:3000/api/v1/posts/${id}/delete`)
dispatch({
type: "DELETING_POST_SUCCESS",
data: deletedPost
})
} catch(error) {
dispatch({
type: "DELETING_POST_FAILURE",
data: { error: "Something went wrong" }
})
}
}
}
Should be something like this:
const mapDispatchToProps(dispatch) {
return bindActionCreators({ deletePost }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Cards)
And then call it as a prop:
onClick={this.props.deletePost(_id)}
import React, { Fragment, useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { getPosts } from '../redux/actions/#posts';
import PostItem from '../components/posts/PostItem';
import CommentForm from '../components/posts/CommentForm';
import Comment from '../components/posts/Comment';
import '../styles/posts/postComponent.scss';
const Posts = ({
getPosts,
posts: { posts, isLoading },
isAuthenticated,
user
}) => {
useEffect(() => {
getPosts();
}, []);
const [show, toggleShow] = useState(false);
console.log(show);
const postsList = isLoading ? (
<div>posts are loading</div>
) : (
posts.map(post => {
return (
<div className='post'>
<PostItem
key={post._id}
auth={isAuthenticated}
user={user}
id={post._id}
title={post.title}
body={post.text}
author={post.name}
avatar={post.avatar}
date={post.date}
likes={post.likes}
comments={post.comments.map(comment => comment)}
toggleShow={toggleShow}
show={show}
/>
<CommentForm id={post._id} />
{post.comments.map(
comment =>
show && (
<Comment
key={comment._id}
comment={comment}
auth={isAuthenticated}
admin={user}
show={show}
/>
)
)}
</div>
);
})
);
return (
<Fragment>
<Link to='/add-post'>add Post</Link>
<div>{postsList}</div>
</Fragment>
);
};
Posts.propTypes = {
getPosts: PropTypes.func.isRequired,
posts: PropTypes.object.isRequired,
isAuthenticated: PropTypes.bool.isRequired
};
const mapStateToProps = state => {
// console.log(state.posts.posts.map(post => post.likes));
// console.log(state);
return {
posts: state.posts,
isAuthenticated: state.auth.isAuthenticated,
user: state.auth.user
};
};
export default connect(mapStateToProps, { getPosts })(Posts);
import React, { Fragment } from 'react';
import '../../styles/posts/postComponent.scss';
const Comment = ({
comment: { user, avatar, name, date, text },
admin,
auth
}) => {
return (
<Fragment>
<div className='c-container'>
<div className='c-img-text'>
<img className='c-img' height={'40px'} src={avatar} />
<div className='c-nt'>
<a href='#' className='c-n'>
{name}
</a>
<span className='c-t'> {text}</span>
<i className='c-d'>{date}</i>
</div>
{auth && admin
? admin._id === user && <div className='c-toggle'>...</div>
: ''}
</div>
</div>
</Fragment>
);
};
export default Comment;
I have a list of posts stored in redux, and mapped through it to create components. Now each component has a some body and comments.
I want to show the comments only after onClick event .
Below is the code I have come up with , and on Click it is toggling all the comments of all the Components.How can I toggle comments of an individual Component.
I have the problem when I use the Reactjs, I'm really new to Reactjs, so maybe it's a easy problem
I want to use the class ClickButton in the UserInfo,but I don't know how to change the name through props
import React, { PropTypes } from 'react';
import { Button } from 'antd';
import { connect } from 'react-redux';
import styles from './ClickButton.less';
const ClickButton = ({ todos,dispatch }) => {
const userinforclick = () => {
dispatch({
type: 'todos/clickbutton',
payload: !todos['click_button'],
});
};
return (
<span>
< span type="primary" className={ styles.show } onClick={ userinforclick.bind(this) } > {this.props.name} < /span >
</span>
);
};
function clickbutton({ todos }){
return{
todos:todos,
}
}
export default connect(clickbutton)(ClickButton)
and i use the ClickButton in UserInfo:
import React from 'react'
import styles from './Userinfo.less'
import ClickButton from '../../components/Button/ClickButton'
import { connect } from 'react-redux';
import { Spin } from 'antd'
const Userinfo = ({ todos,dispatch }) => {
const { userinfo, userinfoloading, click_button } = todos;
if(userinfoloading) {
return <Spin />;
}
const renderList = () => {
return(
<div className={ styles.userInfodiv}>
<div>
<span className={ styles.userInfoTitle }>userinfo</span>
</div>
<div className = { styles.slice }></div>
<div className = { styles.userInfoBody}>
<div className = { styles.userInfoSubBody }>
<span>username:</span>
<span>{userinfo[0]['username']}</span>
</div>
<div className = { styles.userInfoSubBody }>
<span>number:</span>
{ click_button ? <span>{userinfo[0]['phone']}</span> : <input type="text" value={userinfo[0]['phone']} /> }
<ClickButton name="john" />
</div>
</div>
</div>
);
};
return (
<div>
{ renderList() }
</div>
);
};
function mapStateToProps({ todos }) {
return {
todos: todos,
};
}
export default connect(mapStateToProps)(Userinfo);
Here's something that actually works (although I removed the todos and such but you can add them in easily):
class RenderList extends React.Component {
render() {
return (<span> {this.props.name} </span>);
}
}
class App extends React.Component {
render() {
return (<div>
<RenderList name="John"/>
</div>)
}
}
ReactDOM.render(<App/>,document.getElementById("app"));