react redux two components - reactjs

I have a component that will display the products which is coming from backend and a component that receives the products to filter but I have doubt that receive by redux my product list.
should i put for my filters component receive?
or should return the same as I get in my product component?
or should I create an action to filter what I need already?
my home:
return (
<div className="container">
<h1>Shopping</h1>
<hr />
<div className="row">
<div className="col-md-3"><Filters/></div>
<div className="col-md-9"><Products/></div>
</div>
</div>
)
my component products:
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { ProductsFetchData } from '../../store/actions/productsFetch';
import util from '../../util';
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchData('/products');
}
render() {
const productItems = this.props.products.map( product => (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.Products,
hasErrored: state.ProductsHasErrored,
isLoading: state.ProductsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(ProductsFetchData())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
my components filter
import React, { Component } from 'react';
import './style.css'
class FilterHome extends Component {
render() {
return (
<>
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<p>Stat Trak</p>
<p>Normal</p>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<p>Factory New ()</p>
<p>Minimal Wear ()</p>
<p>Field Tested ()</p>
<p>Well Worn ()</p>
<p>Battle Scarred ()</p>
</div>
</div>
</div>
</>
)
}
}
export default FilterHome;

1.redux-state: this is the registering point for all your api responses(all the data from back-end is stored here as prestine and is available as props to any container when you mapStateToProps).
2.local-state: this lives only in your container and all it's child components.
3.filter:
a)from server:
you make a request to the server and get a response of filtered products. this
is more practical.
eg: you have /products?page=1 and you want to search it by some category, let's
say by a specific company. with the data you have at the moment(page 1), you
may have only let's say 1 or even no product relevant to that company, but in fact there are n-numbers of products of the same company available at the server. so this can only be assumed as the
most practical way.
b)filtering from the local-state:
if this is what your'e trying to achieve,
1. you need only one container, HomeProducts
2. make ProductItems as a component. wer'e gonna reuse this component to render both.
**you wrote your filter as an independent container. but those filter functionality should be availabe inside the home page itself. isn't it, i mean you're filtering from the home page itself not from another page. if so, add it to the home page itself
1.HomePage
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { ProductsFetchData } from '../../store/actions/productsFetch';
import util from '../../util';
import ProductItems from '<<..your relative path>>'
import FilterHome from '<<..your relative path>>'
class HomeProducts extends Component {
constructor(props) {
super(props)
this.state = {
productList: null,
}
}
componentDidMount() {
//this.props.fetchData('/products');
this.props.fetchData('page=1');
}
componentWillReceiveProps(nextProps) {
const { productList } = this.state
const { products } = this.props
// this only handles when local state is empty. add your logic here..
!productList && this.setState(prevState => ({
...prevState,
productList: products,
}))
}
handleFilter = (category) => {
// if it's an api call
const { fetchData } = this.props
fetchData(`page=1&category=${category}`)
//or you simply want to filter this local state(not-recommended)
const { products } = this.props
const productsList = [...products].filter(item => item.category === category)
this.setState(prevState => ({
...prevState,
productsList,
}))
}
render() {
const { productList } = this.state
const { handleFilter } = this
return (
<div className="container">
<FilterHome {...{ handleFilter }} />
<ProductItems {...{ productsList }} />
</div>
)
}
}
const mapStateToProps = (state) => {
return {
products: state.Products,
hasErrored: state.ProductsHasErrored,
isLoading: state.ProductsIsLoading
};
};
//it may not suit depends on your api, but you get the idea..
const mapDispatchToProps = (dispatch) => {
return {
// fetchData: () => dispatch(ProductsFetchData())
fetchData: params => dispatch(ProductsFetchData(`/products?${params}`))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
2.ProductItems
import React from 'react'
const ProductItem = ({ product }) => {
return (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
}
const ProductItems = ({ productList }) => {
return (
<div className="row">
{productList && productList.map(product => <ProductItem key={product.id} {...{ product }} />)}
</div>
)
}
export default ProductItems
3.FilterHome
import React from 'react'
const FilterHome = ({ handleFilter }) => {
return (
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<a href="" className="text-decoration-none" onClick={() => handleFilter('stat_trak')}><p>Stat Trak</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('normal')}><p>Normal</p></a>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<a href="" className="text-decoration-none" onClick={() => handleFilter('factory_new')}><p>Factory New ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('minimal_wear')}><p>Minimal Wear ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('field_tested')}><p>Field Tested ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('well_worn')}><p>Well Worn ()</p></a>
<a href="" className="text-decoration-none" onClick={() => handleFilter('battle_scarred')}><p>Battle Scarred ()</p></a>
</div>
</div>
</div>
)
}
export default FilterHome
i roughly re-wrote it, may contain bugs..

first add it to the local state and employ a call back to the filter component..
handleFilter = (category) => {
const { Products } = this.state
const products = {...Products}
//or depends on the type, so it wont mutate the real data>> const products = [...Products]
return products.filter(item => item.category === category)
}
this is what is understood from your comment. is that it?

Related

React Error: Cannot read property 'map' of undefined

I have been trying to resolve this error for almost 2 hours but no luck. I have even researched and used the bind method but still no luck with mapping a props that was passed through a parent component. Your help will be greatly appreciated.
import React from "react";
import { Link } from "react-router-dom";
const PostList = ({ postItem }) => {
postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
<Link to="/create">
<ion-icon
style={{ color: "#fc5185", fontSize: "20px" }}
name="trash-outline"
></ion-icon>
</Link>
</div>
</div>
));
};
export default PostList;
And the parent component is
class Dashboard extends Component {
state = {
posts: [
{
id: 1,
title: "Hello",
comment: "it is sunny today",
},
],
};
createPost = (title, comment) => {
const newPost = {
id: Math.floor(Math.random() * 1000),
title,
comment,
};
this.setState({
posts: [...this.state.posts, newPost],
});
};
render() {
return (
<div>
<CreatePost createPost={this.createPost} />
<PostList postItem={this.state.posts} />
</div>
);
}
}
export default Dashboard;
I guess you have missed to add return to the PostList component, you can do it in three ways (read about arrow functions)
const PostList = ({ postItem }) => postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
</div>
</div>
));
const PostList = ({ postItem }) => (
postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
</div>
</div>
));
);
const PostList = ({ postItem }) => {
return postItem.map((post) => (
<div className="mx-auto mb-3 card w-75" key={post.id}>
<div className="card-body">
<h5 className="card-title">{post.title}</h5>
<p className="card-text">{post.comment}</p>
</div>
</div>
));
}
Here is the working example

Passing methods between Functional components in react

I have 2 components as below. When I try to pass a method (increment) from the component 'CartList' to 'CartItem' it says increment is undefined. The error occurs when I click on the button (Pointed in the below code). How can I solve this error?
Parent
import React, {Component} from 'react';
import CartItem from './CartItem';
import {connect} from "react-redux";
import Axios from "axios";
const mapStateToProps = ({ session}) => ({
session
});
const CartList = ({session, ...props}) => {
const cart = props.cart;
const increment = (productId) => {
const item = {
userId : session.userId,
productId: productId
};
Axios.post('http://localhost:5000/api/cart/increment', item)
.then(res=>{
if(res.status === 200){
console.log('Incremented');
}
})
};
return (
<div className="container-fluid">
{cart.map(item => {
return <CartItem key = {item.id} item={item} increment={increment}/>
})}
</div>
);
};
export default connect(
mapStateToProps
)(CartList);
Child
import React from 'react';
import {connect} from "react-redux";
const mapStateToProps = ({ session}) => ({
session
});
const CartItem = ({session ,...props}) => {
const {id,name, price, quantity} = props.item;
const {increment} = props.increment;
return (
<div className="row my-2 text-capitalize text-center">
<div className="col-10 mx-auto col-lg-2">
<img style={{width: '5rem', height: '5rem'}} className="img-fluid" alt="product "/>
</div>
<div className="col-10 mx-auto col-lg-2">
<span className="d-lg-none">Product: </span>{name}
</div>
<div className="col-10 mx-auto col-lg-2">
<span className="d-lg-none">Price: </span>{price}
</div>
<div className="col-10 mx-auto col-lg-2 my-2 my-lg-0">
<div className="d-flex justify-content-center">
<div>
<span className="btn btn-black mx-1" >-</span>
<span className="btn btn-black mx-1">{quantity}</span>
<span className="btn btn-black mx-1" onClick={() => increment(id)}>+</span> //<- Error occurs if I click on this button
</div>
</div>
</div>
<div className="col-10 mx-auto col-lg-2">
<div className="cart-icon" >
<i className="fas fa-trash"/>
</div>
</div>
<div className="col-10 mx-auto col-lg-2">
<srong>Total: ${50}</srong>
</div>
</div>
);
};
export default connect(
mapStateToProps
)(CartItem);
I tried implementing the function directly in the child component it is working fine. This method is responsible for incrementing the quantity of a product in the database(MongoDB). But updated value does not display. Because of that I implemented the 'increment' function within the parent component
The issue is this line:
const {increment} = props.increment;
That is trying to access props.increment.increment, which is undefined. You either meant to do
const increment = props.increment;
or
const {increment} = props;
Hi Please replace this line
Because when you destructuring props you don't need map key from props
for better understanding console your props or have a look at this document.
https://medium.com/#lcriswell/destructuring-props-in-react-b1c295005ce0
const {increment} = props.increment;
instead of
const {increment} = props;
I hope it works.
Thanks

Todo App in React- Wanted to add button which when clicks deletes the whole todo list

I have created a ToDo App in React. I want to add a single button which when I clicked on removes the whole todo list and shows the message to the user "You don't have any todo's". I am trying to add functionality but can't seem to find a perfect way.
I have given all the Todos a unique id and I also to grab these id's but don't how to use them to remove all Todos from a single button only. Help me. Thanks in advance
here is my main component App.js
import React, { Component } from 'react';
import PrintTodo from "./printtodo"
import Addtodo from "./addTodo"
class App extends Component {
state = {
todos: [
{id:1, content:"Buy Tomatoes"},
]
}
deleteTodo = (id) => {
const todos = this.state.todos.filter(todo => {
return todo.id !== id
})
this.setState({
todos
})
}
addTodo = (todo) => {
todo.id = Math.random()
// console.log(todo)
let todos = [...this.state.todos, todo]
this.setState({
todos
})
}
button = () => {
// console.log(this.state)
const allTodos = this.state.todos.filter(todo => {
console.log(todo)
})
// const id = 10;
// console.log(allTodos)
// allTodos.forEach(todo => {
// // console.log(todo)
// const arr = new Array(todo)
// arr.pop()
// })
}
render(){
// console.log(this.state)
return (
<div className="App">
<div className="container">
<header className="text-center text-light my-4">
<h1>ToDo - List</h1>
<form>
<input type="text" name="search" placeholder="Search ToDo's" className="form-control m-auto"/>
</form>
</header>
<PrintTodo addTodo={this.state.todos} deleteTodo={this.deleteTodo}/>
<Addtodo addTodo={this.addTodo} allTodos={this.button}/>
</div>
</div>
)
}
}
export default App;
PrintTodo Component
import React from 'react'
const printTodo = ({addTodo, deleteTodo, }) => {
// console.log(addTodo)
const todoList = addTodo.length ? (
addTodo.map(todo => {
return (
<ul className="list-group todos mx-auto text-light" key={todo.id}>
<li className="list-group-item d-flex justify-content-between align-items-center">
<span>{todo.content}</span>
<i className="far fa-trash-alt delete" onClick={()=>{deleteTodo(todo.id)}}></i>
</li>
</ul>
)
})
) : (
<p className="text-center text-light">You don't have any ToDo's</p>
)
return (
<div>
{todoList}
</div>
)
}
export default printTodo
AddTodo Component
import React, { Component } from 'react'
class Addtodo extends Component{
state = {
content: ""
}
handleChange = (e) => {
this.setState({
content: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault()
this.props.addTodo(this.state)
this.setState({
content: ""
})
}
render(){
// console.log(this.props.allTodos)
return(
<div>
<form className="text-center my-4 add text-light" onSubmit={this.handleSubmit}>
<label htmlFor="add">Add a New ToDo</label>
<input onChange={this.handleChange} type="text" name="add" id="add" className="form-control m-auto" value={this.state.content}/>
</form>
<button onClick={() => {this.props.allTodos()}}>Clear Whole List</button>
</div>
)
}
}
export default Addtodo
In your app.js make this your button component.
button = () => {
this.setState({todos: []})
})
Resetting your todos to an empty array will delete all your todos.

Moving an item from Todolist to delete list and delete it from TodoList

Hello i want to move an item from todo list to delete list in ReactJS and i want to delete it from todo list when i move it to done list i did everything i can delete the selected item or all items but i cant add it to done list when i move it
import React from 'react';
import './App.css';
import Todoinput from './Components/Todoinput'
import Todolist from './Components/Todolist'
import Tododone from './Tododone'
import { render } from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import uuid from 'uuid';
class App extends React.Component {
state= {
items:[],
id:uuid(),
item:'',
editItem:false
}
handleChange = (e) => {
this.setState ({
item:e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault ();
const newItem = {
id:this.state.id,
title:this.state.item,
};
const updatedItems = [...this.state.items,newItem]
this.setState ({
items:updatedItems,
item:'',
id:uuid(),
editItem:false
})
}
clearList = (e) => {
this.setState ({
items:[]
})
}
doneItem = (id) => {
const doneItems = this.state.items.filter (item => item.id !== id);
this.setState ({
items:doneItems,
})
}
handleEdit = (id) => {
const doneItems = this.state.items.filter (item => item.id !== id);
const selectedItem = this.state.items.find(item=> item.id === id)
console.log(selectedItem)
this.setState ({
items:doneItems,
item:selectedItem.title,
editItem:true,
id:id
})
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-10 mx-auto col-md-8 mt-4">
<h3 className="text-capitalize text-center">Todo Inputs</h3>
<Todoinput item={this.state.item} handleChange={this.handleChange}
handleSubmit={this.handleSubmit}
editItem={this.state.editItem}
/>
<Todolist items={this.state.items} clearList={this.clearList} doneItem={this.doneItem} handleEdit={this.handleEdit}/>
<Tododone doneItem={this.doneItem} />
</div>
</div>
</div>
);
}
}
export default App;
/**/
import React from 'react'
import Todoitem from './Todoitem'
class Todolist extends React.Component {
render() {
const {items,clearList,doneItem,handleEdit}=this.props
return (
<ul className="list-group my-5">
<h3 className="text-capitalize text-center">todo list</h3>
{
items.map(item => {
return (
<Todoitem
key={item}
title={item.title}
doneItem={()=> doneItem(item.id)}
handleEdit={()=> handleEdit(item.id)}
/>
)
})
}
<button type="button" className="btn btn-danger btn-block text-capitalize mt-5"
onClick={clearList}
>clear list</button>
</ul>
)
}
}
export default Todolist
/**/
import React from 'react'
class Todoinput extends React.Component {
render() {
const {item,handleChange,handleSubmit,editItem} = this.props
return (
<div className="card card-body my-3">
<form onSubmit={handleSubmit}>
<div className="input-group">
<div className="input-group-prepend">
<div className="input-group-text bg-primary text-white">
<i className="fa fa-book" ></i>
</div>
</div>
<input type="text" className="form-control text-capitalize" placeholder="Add A To Do Item"
value={item}
onChange={handleChange}
/>
</div>
<button type="submit"
className={editItem ? "btn btn-block btn-success mt-3" : "btn btn-block btn-primary mt-3" }>
{editItem ? 'Edit Item' : "Add Item"}</button>
</form>
</div>
)
}
}
export default Todoinput
/**/
import React from 'react'
class Todoitem extends React.Component {
render() {
const {title,doneItem,handleEdit} = this.props
return (
<li className="list-group-item text-capitalize d-flex justify-content-between my-2">
<h6>{title}</h6>
<div className="todo-icon">
<span className="mx-2 text-success" onClick={handleEdit}>
<i className="fa fa-edit"></i>
</span>
<span className="mx-2 text-danger"onClick={doneItem}>
<i className="fa fa-window-close"></i>
</span>
</div>
</li>
)
}
}
export default Todoitem
/**/
import React from 'react'
class Tododone extends React.Component {
render() {
const {items,clearList,doneItem,title,item}=this.props
return (
<div>
<h2 className="text-capitalize text-center">Done Items</h2>
<li className="list-group-item text-capitalize d-flex justify-content-between my-2">
<h6>{item}</h6>
<div className="todo-icon">
<span className="mx-2 text-danger" onClick={doneItem}>
<i className="fa fa-trash"></i>
</span>
</div>
</li>
<button type="button" className="btn btn-danger btn-block text-capitalize mt-5"
onClick={clearList}>clear list</button>
</div>
)
}
}
export default Tododone
so if anyone can help Please i post all the code above if anyone can help me please <
To track if a task is done, add a isDone field to item, when you say it's done, flag it to true.
So when you create a new item:
const newItem = {
id:this.state.id,
title:this.state.item,
isDone: false
};
When you render items that are not done, filter through isDone===false, like this:
<Todolist items={this.state.items.filter(item => item.isDone === false)} ... />
When you delete an item from the ToDoList you want it to go to the DoneList, so you set your doneItem function like this:
doneItem = id => {
const newItems = [...this.state.items];
const item = newItems.find(item => item.id === id);
item.isDone = true;
this.setState({
items: newItems
});
}
When you render the done list, filter through isDone === true, like this:
<Tododone items={this.state.items.filter(item => item.isDone === true)} ... />
Now get the items prop in Tododone and map it to see the done items, this below is just an example:
{items.map(item => (
<li className="list-group-item text-capitalize d-flex justify-content-between my-2">
<h6>{item.title}</h6>
<div className="todo-icon">
<span className="mx-2 text-danger" onClick={doneItem}>
<i className="fa fa-trash"></i>
</span>
</div>
</li>
))}
Since I don't know what you want to do with those divs and spans I'll leave them like you set them.
When you click on clear list I assume you want to delete only the todoList or only the doneList, I suggest you to pass a flag to tell App which list to clear.
Here's the sandbox with your code that solves your problem.
down and dirty because I have to head to work would be to add something like doneItemsArray: [] in your state and then just do another filter in your doneItem method.
doneItem = id => {
const filteredTodos = this.state.items.filter(item => item.id !== id);
const doneItem = this.state.items.filter(item => item.id === id);
this.setState({
items: doneItems,
doneItemsArray: [...this.state.doneItemsArray, doneItem]
});
};

React Sort By Like

I am trying to figure out how to add an onClick feature that will then sort the likes in descending order. AKA each project has a 'like' button. I want to add another button to the page to allow the user to sort the project likes by descending order.
import React from 'react';
import ProjectsListItem from './ProjectsListItem'
const Project = ({ projects }) => {
const renderProjects = projects.projects.map(project =>
<ProjectsListItem project={project} key={project.id}/>
);
return (
<div className="container">
<div className="row">
{renderProjects}
</div>
</div>
);
};
export default Project;
Page 2
class ProjectsListItem extends Component {
handleOnClick = () => {
this.props.likeProject(this.props.project)
}
onClick = () => {
this.props.sortBy(this.props.project.like)
}
render() {
return(
<div>
<div className="col-sm-4">
<div className="container-fluid text-left">
<h4> <Link key={this.props.project.id} to=
{`/projects/${this.props.project.id}`}>{this.props.project.title}
</Link> </h4>
<h5> {this.props.project.studio}</h5>
<CounterButton project={this.props.project} likeProject=
{this.handleOnClick}/>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
projects: state.projects
}
}
export default connect(mapStateToProps, {likeProject})
(ProjectsListItem);
You would have to make an event handler such as
https://reactjs.org/docs/handling-events.html
In this case you would probably want to do
onSortClick(e) {
e.preventDefault();
this.props.sorted = true
}
bind that to your click handler like this:
<CounterButton project={this.props.project} likeProject=
{this.onSortClick.bind(this)}/>
Hope this helps.

Resources