adding counter to redux state - javascript

I am trying to add a like increment count using redux to my app, but I am not what I have to do I have been tampering with it for the last few days and have gotten nowhere. I wanted to use my comments from my shared folder as the state that I wanted to alter on the store. Any help would be appreciated! Thanks
this is my store:
import { createStore, combineReducers } from 'redux';
import { Comments } from './comments';
import { counterReducer } from './counter'
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
counter : counterReducer,
comments: Comments
}
));
return store;
};
action creators:
import * as ActionTypes from './ActionTypes';
export const addComment = (commentId, author, text, likes, date) => ({
type: ActionTypes.ADD_COMMENT,
payload: {
commentId : commentId,
author: author,
text: text,
likes: likes,
date: date
}
});
export const likeComment = (commentId, likes) => ({
type: ActionTypes.LIKE_COMMENT,
payload: {
commentId: commentId,
likes: likes
}
});
my counter reducer for the likes (i am using my COMMENTS as my state that has the commentId, comments, author, dates, likes):
import * as ActionTypes from './ActionTypes';
import { COMMENTS } from '../shared/comments';
export const counterReducer = (state = COMMENTS, action) => {
switch(action.type) {
case ActionTypes.LIKE_COMMENT:
const comment = action.payload
comment.id = state.length
comment.like = state.likes + 1
return state.concat(comment)
default:
return state;
}
};
This is the main component:
import React, { Component } from 'react';
import TopComment from './TopComment';
import DisplayComment from './PostComment';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { addComment, likeComment } from '../redux/ActionCreators';
const mapStateToProps = state => {
return {
comments: state.comments,
likes: state.likes
}
};
const mapDispatchToProps = {
addComment: (commentId, author, text, likes, date) => (addComment(commentId, author, text,
likes, date)),
likeComment: (likes) => (likeComment(likes))
}
class Main extends Component{
render(){
return(
<div className="comment-box">
<h1>Join the discussion!</h1>
<h1>Adam's post:</h1>
<TopComment/>
<DisplayComment
addComment = {this.props.addComment}
likeComment ={this.props.likeComment}
comments = {this.props.comments}
/>
</div>
)
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
post comment component that will add and display each comment that has been entered
through the input:
import React, { Component } from 'react';
import { Button, Card, CardBody, CardTitle, CardText } from 'reactstrap';
import { Control, LocalForm, Errors} from 'react-redux-form';
// will check validation
const required = val => val && val.length;
const maxLength = len => val => !val || (val.length <= len);
const minLength = len => val => val && (val.length >= len);
// will display comment when posted
function RenderComments({comments}) {
function handleLike(){
this.props.likeComment(this.props.likeComment)
}
if (comments) {
return (
<div className="col-sm-12">
<h4>Comments</h4>
{
comments.map(comment => {
return (
<div className="container">
<div className="row">
<div className="col-sm-12 ">
<Card style={{
backgroundColor: '#212d40',
borderColor: '#fff',
borderRadius: '0.25rem',
padding: '0.5rem 0.5rem 0.5rem',
marginBottom: '20px'
}}>
<CardBody>
<img src="./images/random.jpeg" alt="random" height="50px" />
<CardTitle tag="h3">{comment.author}</CardTitle>
<CardText>{comment.text}, <br/>
likes: {comment.likes} <br/>
{new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: '2-digit'}).format(new Date(Date.parse(comment.date)))}
</CardText> <br/>
<Button onClick={handleLike}>Like </Button>
</CardBody>
</Card>
</div>
</div>
</div>
);
})
}
</div>
);
}
return <div/>;
}
class PostComment extends Component {
handleSubmit(values) {
this.props.addComment(this.props.commentId, values.author, values.text, values.date);
}
render(){
return(
<div>
<LocalForm className="comment-form" onSubmit={ values =>
this.handleSubmit(values) }>
<div className="form-group">
<Control.text
model=".author"
className="form-control"
placeholder="name"
validators={{
required,
minLength: minLength(2),
maxLength: maxLength(15)
}}
/>
<Errors
className="text-danger"
model=".author"
show="touched"
component="div"
messages={{
required: 'Required',
minLength: 'Must be at least 2 characters',
maxLength: 'Must be 15 characters or less'
}}
/>
</div>
<div className="form-group">
<Control.text
model=".text"
className="form-control"
placeholder="comment"
/>
</div>
<Button className="btn-color" color="primary" type="submit">Submit</Button>
</LocalForm>
</div>
)
}
}
function DisplayComment(props){
return(
<div>
<div className="col">
<RenderComments
addComment = {props.addComment}
deleteComment = {props.deleteComment}
comments = {props.comments}
likeComment = {props.likeComment}
/>
</div>
<div className="col">
<PostComment
addComment = {props.addComment}
comments = {props.comments}
likeComment= {props.likeComment}
/>
</div>
</div>
)
}
export default DisplayComment
this is my commments from my shared file that has all the comment data (likes, comment, date, etc):
export const COMMENTS = [
{
commentId: 0,
author: 'Richard',
text: 'I love disum!',
likes: 1,
date: "2020-07-01T19:44Z"
},
{
commentId: 1,
author: 'Jake Paul',
text: 'Fried foods are the best!',
likes: 0,
date: "2020-09-20T19:44Z"
}
]

You're mixing the source of truth for comment likes. Should the likes count be part of the comment? Or should it be it's own thing?
Let's KISS and just keep the likes count as part of the comment as you have already set up - get rid of your other counter reducer altogether.
Your comments reducer should now look like this:
switch(action.type) {
case ActionTypes.ADD_COMMENT:
// your existing code to handle adding a comment to your state
// this new case increments the likes for a given comment
case ActionTypes.LIKE_COMMENT:
return state.comments.map(c => c.commentId === action.payload.commentId ? ({ ...c,likes: c.likes+1 }) : c);
default:
return state;
}
};

Related

Add To Cart functionality not working - Context API - React

I am following this tutorial here and followed it exactly, and I have even gone over github repo and code is the same. However when I click my button to add the product to the cart the state does not change. In react dev tools only state that changes is showHideCart changes from false to true - so it seems to only recognise that state change?
I want to be able to add my product to the basket once I click the button - can anyone see where the I have gone wrong? there are no errors in the console and the basket array just does not change it does not even say undefined which was what I thought would of been the case for no product showing up in basket.
Here is a link to a code sandbox
and below is code files where I believe the issues would be:
CartState.js
import { useReducer } from 'react'
import { CartContext } from './CartContext'
import {CartReducer} from './CartReducer'
import { SHOW_HIDE_CART, ADD_TO_CART, REMOVE_ITEM } from '../Types'
export const CartState = ({children}) => {
const initialState ={
showCart: false,
cartItems: [],
};
const [state, dispatch] = useReducer(CartReducer, initialState);
const addToCart = (item) => {
dispatch({type: ADD_TO_CART, payload: item})
};
const showHideCart = () => {
dispatch({type: SHOW_HIDE_CART})
};
const removeItem = (id) => {
dispatch({ type: REMOVE_ITEM, payload: id });
};
return (
<CartContext.Provider
value={{
showCart: state.showCart,
cartItems: state.cartItems,
addToCart,
showHideCart,
removeItem,
}}>
{children}
</CartContext.Provider>
)
};
CartReducer.js
import {ADD_TO_CART, REMOVE_ITEM, SHOW_HIDE_CART } from '../Types'
export const CartReducer = (state, action) => {
switch (action.type) {
case SHOW_HIDE_CART: {
return{
...state,
showCart: !state.showCart
}
}
case ADD_TO_CART: {
return {
...state,
cartItems: [...state.cartItems, action.payload],
}
}
case REMOVE_ITEM: {
return {
...state,
cartItems: state.cartItems.filter(item => item.id !== action.payload)
}
}
default:
return state
}
}
Product.js
import React, { useContext } from 'react'
import { QuantityButtonDiv } from './QuantityButtonDiv'
import {BasketItem} from './BasketItem'
import { CartContext } from '../context/cart/CartContext'
export const Product = ({product}) => {
const {addToCart } = useContext(CartContext)
return (
<div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={product.image}/>
</div>
<div className="details-div">
<h1>{product.title}</h1>
<span>
{product.description}
</span>
<span className="price">
£ {product.price}
</span>
<div className="stock-div">
{product.stock} in stock
</div>
<QuantityButtonDiv/>
<button onClick={() => addToCart(product)} className="button">
Add To Basket
</button>
</div>
</div>
)
}
ProductDetailsPAge.js
import React, { useContext } from 'react';
import '../styles/productDetailsPage.scss';
import img from '../assets/image.png'
import { QuantityButtonDiv } from '../components/QuantityButtonDiv';
import { Product } from '../components/Product';
import { CartContext } from '../context/cart/CartContext';
export const ProductDetailsPage = () => {
const products = [
{
id: 1,
title: "Waxed Cotton Hooded Jacket",
image: require("../assets/image.png"),
description: "The Drumming jacket in orange is finished with a water-repellent dry wax treatment that creates a love-worn look. It's made in the United Kingdom using organic cotton ripstop with a drawstring hood, underarm eyelets and buttoned flap front pockets. Shoulder epaulettes add a utilitarian twist, while a fly-fronted zip and snap-button closure keeps the overall look streamlined. Attach one of the collection's padded liners to the internal tab on cooler days.",
price: 650,
stock: 10,
}
]
return (
<div className="product-details-page">
{
products.map((product) => (
<Product
key={product.id}
product={product}
// title={product.title}
// image={product.image}
// description={item.description}
// price={item.price}
// stock={item.stock}
/>
))
}
</div>
)
}
There is currently an error on basketpage route
You are passing an object through CartContext:
<CartContext.Provider
value={{
showCart: state.showCart,
cartItems: state.cartItems,
addToCart,
showHideCart,
removeItem,
}}>
in BasketPage you are destructuring an array:
const [cart, setCart] = useContext(CartContext);//Typeerror here
Also, you aren't passing neither cart or setCart through that Context value object.
You need to first solve this error and further see if it works alright.
Issue was with showCart and showHideCart - once I got rid of these the cart was working.

How to update quantity when item is added to cart rather than add same item twice - Context API - Reaxt

I only have one item for my basket app, when I add it multiple times it adds multiple different basketItems all with same id and with same quantity so for example 3 items all of quantity 3. What I want however is for there just to be one item with a quantity of 3.
I also want to be able to remove the item from cart by quantity - so one at a time. however currently when I click to remove from cart it removes it entirely from cart even if quantity in cart is more than 1.
How do I do this?
code sandbox [here] (https://codesandbox.io/s/silly-jepsen-ix3dd?file=/src/pages/ProductDetailsPage.js&resolutionWidth=584&resolutionHeight=696)
code below:
CartReducer.js
import {ADD_TO_CART, CHANGE_CART_QUANTITY, DECREASE, REMOVE_FROM_CART} from '../Types'
export const CartReducer = (state, action) => {
switch (action.type) {
case ADD_TO_CART: {
return {
...state,
cart: [...state.cart, { ...action.payload, qty : 1}]
};
}
case REMOVE_FROM_CART: {
return {
...state,
cart: state.cart.filter((c) => c.id !== action.payload.id,)
};
}
default:
return state
}
}
CartContext.js
import { createContext, useContext, useReducer } from "react";
import { CartReducer } from "./CartReducer";
import { products } from "../../pages/ProductDetailsPage";
const Cart = createContext();
const Context = ({ children }) => {
const [state, dispatch] = useReducer(CartReducer, {
products: products,
cart: [],
});
// const [productState, productDispatch] = useReducer(productReducer, {
// byStock: false,
// byFastDelivery: false,
// byRating: 0,
// searchQuery: "",
// });
// console.log(productState);
return (
<Cart.Provider value={{ state, dispatch }}>
{children}
</Cart.Provider>
);
};
export const CartState = () => {
return useContext(Cart);
};
export default Context;
BasketItem.js
import React, { useContext } from 'react'
import image from '../../assets/image.png'
// import { QuantityButtonDiv } from '../components/QuantityButtonDiv'
import plusButtonImage from '../../assets/vector+.png'
import subtractButtonImage from '../../assets/vector.png'
import { CartState } from '../../context/cart/CartContext'
import { ADD_TO_CART, DECREASE, REMOVE_FROM_CART } from '../../context/Types'
import CustomizedSelect from '../SelectInput'
export const BasketItem = ({item}) => {
// const { cartItems, removeItem } = useContext(CartContext);
const {
state: { cart },
dispatch,
} = CartState();
return (
<div className="basket-item">
<div className="title-div">
<span>
{item.title}
</span>
</div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={image}/>
</div>
<div className="price-div">
<span>
£{item.price}
</span>
</div>
<div className="basket-quantity-div">
<button onClick={() => dispatch({
type: REMOVE_FROM_CART,
payload: item,
})} className="subtract-btn">
<img src={subtractButtonImage}/>
</button>
<span className="quantity-value">
{cart.length}
</span>
<button onClick={() => dispatch({
type: ADD_TO_CART,
payload: item,
})} className="add-btn">
<img src={plusButtonImage}/>
</button>
</div>
<div className="total-div">
£{cart.reduce((amount, item) => item.price + amount, 0)}
</div>
</div>
)
}
Product.js
import React, { useContext, useState } from 'react'
import image from '../../assets/image.png'
import { QuantityButtonDiv } from '../QuantityButtonDiv'
import {BasketItem} from '../basketItem/BasketItem'
import { CartContext, CartState } from '../../context/cart/CartContext'
import { ADD_TO_CART, REMOVE_FROM_CART } from '../../context/Types'
export const Product = ({product}) => {
// const {addToCart, cartItems, removeItem } = useContext(CartContext)
const { state: {cart}, dispatch } = CartState();
const [stockCount, setStockCount] = useState(10)
return (
<div>
<div className="image-div">
<img style={{height: "100%", width: "100%"}} src={image}/>
</div>
<div className="details-div">
<h1>{product.title}</h1>
<span>
{product.description}
</span>
<span className="price">
£ {product.price}
</span>
<div className="stock-div">
{stockCount} in stock
</div>
<QuantityButtonDiv/>
{cart.some((p) => p.id === product.id) ? (
//checking to see if item is in cart if so remove from cart button appears
<button onClick={() => dispatch({
type: REMOVE_FROM_CART,
payload: product,
})} className="remove-button">
Remove From Cart
</button>
) : (
<></>
)}
<button onClick={() => {dispatch({
type: ADD_TO_CART,
payload: product,
}); setStockCount(stockCount-1)}} disable={stockCount <= 0} className="add-to-cart-button">
{stockCount === 0 ? "Out Of Stock" : "Add To Cart"}
</button>
</div>
</div>
)
}
before adding new product to cart, you need to check if the product is already in the cart. You need to check it in ADD_TO_CART case.
in REMOVE_FROM_CART case, check if the quantity is more than 1. If its more than 1, don't remove it.

React list.map is not a function when deleting from local storage

I'm working on a budget tracking app, where the user can add different incomes and expenses. I'm using useReducer to manage state. Each income and expense is an object and when the user submits an income or expense, it is displayed as an income list with income items or expense list with expense items. Each item in the list has a remove button that should remove the income item or expense item. But when I try click the remove button to remove an income item for example, I get an error saying list.map is not a function.
Not exactly sure why this happens, but I think it has something to do with the removeItem function in IncomeOutputList.js. What would be the proper way to remove an income item?
App.js
import React, { useState, useReducer } from 'react';
import './App.css';
import BudgetInput from './components/input/BudgetInput';
import BudgetOutput from './components/output/BudgetOutput';
import IncomeOutputList from './components/output/IncomeOutputList';
import ExpenseOutputList from './components/output/ExpenseOutputList';
const useSemiPersistentState = (key, initialState) => {
// console.log(JSON.parse(localStorage.getItem(key)));
const [value, setValue] = React.useState(
localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
);
React.useEffect(()=>{
localStorage.setItem(key, JSON.stringify(value));
}, [value, key])
return [value, setValue];
};
const initialBudget = {
description: '',
type: '+',
key: 'income',
value: ''
};
const initialState = {
incomes: [{}],
expenses: [{}],
budgetObj: initialBudget
};
const budgetReducer = (state, action) => {
switch(action.type) {
case "CHECK_STATE":
console.log(state); //just to check state on submit
case "ON_DESC_CHANGE":
return {
...state,
budgetObj: {
...state.budgetObj,
description: action.payload
}
}
case "ON_TYPE_CHANGE":
const isExpense = action.payload === '-';
return {
...state,
budgetObj: {
...state.budgetObj,
type: action.payload,
key: isExpense ? 'expense' : 'income'
}
}
case "ON_VALUE_CHANGE":
return {
...state,
budgetObj: {
...state.budgetObj,
value: action.payload,
}
}
case 'SUBMIT_BUDGET':
console.log(state.incomes);
const budget = {...state};
const isIncome = budget.budgetObj.type === '+';
return {
incomes: isIncome ? state.incomes.concat(budget.budgetObj) : state.incomes,
expenses: isIncome ? state.expenses : state.expenses.concat(budget.budgetObj),
budgetObj: initialBudget,
}
case "REMOVE_ITEM":
console.log('test');
return {
...state,
// not sure if the following line is the correct way to remove from local storage
incomes: (index) => JSON.parse(localStorage.getItem("income")).splice(index,1),
// expenses: (index) => JSON.parse(localStorage.getItem("expense")).splice(index,1)
}
default:
return state;
}
}
const useSemiPersistantReducer = (key, initialState) => {
const [value, dispatch] = React.useReducer(
budgetReducer,
localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState
);
React.useEffect(()=>{
localStorage.setItem(key, JSON.stringify(value));
}, [value, dispatch]) //[inocmes, setIncomes]
return [value, dispatch];
}
const App = () => {
const [budgetState, setBudget] = useSemiPersistantReducer(initialState.budgetObj.key,initialState);
const {incomes, expenses, budgetObj, key} = budgetState;
return (
<div className="App">
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css"></link>
<div className="top">
<BudgetOutput />
</div>
<div className="bottom">
<BudgetInput
descValue={budgetObj.description || ''} //event.target.value
onDescChange={event => setBudget({ type: "ON_DESC_CHANGE", payload: event.target.value })}
onSelectChange={event => setBudget({ type: "ON_TYPE_CHANGE", payload: event.target.value })}
type={budgetObj.type || ''}
onBudgetSubmit={ () => setBudget({ type : 'SUBMIT_BUDGET' }) }
budgetValue={budgetObj.value || ''}
onValChange={event => setBudget({ type: "ON_VALUE_CHANGE", payload: event.target.value })}
/>
<div className="container clearfix">
<IncomeOutputList
list={incomes}
removeIncome={ () => setBudget({ type: "REMOVE_ITEM" })} //not sure if this is correct?
/>
<ExpenseOutputList
list={expenses}
// removeExpense={(index)=>removeExp(index)}
/>
</div>
</div>
</div>
)
};
export default App;
IncomeOutput.js
import React from 'react';
import IncomeOutput from './IncomeOutput';
// list will be list of income objects
const IncomeOutputList = ({ list, removeIncome }) => {
return (
<div className="income__list">
<div className="income__list--title">INCOME</div>
{list.map((item, index, arr) => <IncomeOutput
id={item.id}
value={item.value}
type={item.type}
desc={item.description}
// error has something to do with the following line?
handleButton={()=>removeIncome(index)}
/>
)}
</div>
)
}
export default IncomeOutputList;
IncomeOutput.js
import React from 'react';
import ValueOutput from './ValueOutput';
const IncomeOutput = ({ desc, type,id, value, handleButton }) => {
return (
<>
<div className="item clearfix income" id={id}>
<div className="item__description">{desc}</div>
<ValueOutput
type={type}
value={value}
handleClick={handleButton}
/>
</div>
</>
)
}
export default IncomeOutput;
ValueOutput.js
import React from 'react';
import Button from '../buttons/Button';
const ValueOutput = ({type, value, handleClick}) => {
return (
<>
<div className="right clearfix">
<div className="item__value">{type} {value}</div>
<Button buttonType="item__delete--btn" handler={handleClick}/>
</div>
</>
)
}
export default ValueOutput;
Button.js
import React from 'react';
const Button = ({buttonType, handler}) => (
<>
<div className="item__delete">
<button className={buttonType} onClick={handler}>
<i className="ion-ios-close-outline"></i>
</button>
</div>
</>
)
export default Button;
I didn't run your code but just a guess
incomes: (index) => JSON.parse(localStorage.getItem("income")).splice(index,1),
incomes is a function not an array, so "incomes.map()" does not exist.

How do I access a function that is being updated from a redux store properly?

I have this component that I split for easy management. Before splitting everything worked as expected, after splitting I am getting an error when I click on an icon which calls the createReactionsIcon. Error says
TypeError: updateReaction is not a function
onClick
./components/home/components/SingleUpload.jsx:26
23 |
24 | return icons.map(({ key, text, type }) => (
25 | <IconText
> 26 | onClick={() => updateReaction(item.id, key)}
| ^ 27 | key={key}
28 | type={type}
29 | text={text}
How can I access this correctly from my Home component where updateReaction is returning updateReaction from the redux store.
SubComponent
import PropTypes from 'prop-types';
import React from 'react';
import { Avatar, Card, Icon, List } from 'antd';
import { LIST_TEXTS, STYLES } from '../constants';
const { AVATAR, CARD_CONTAINER, CARD_LIST, ICON, USER_LIST } = STYLES;
const { INNER, MORE, UPLOAD, VERTICAL } = LIST_TEXTS;
const IconText = ({ type, text, onClick }) => (
<span>
<Icon type={type} style={ICON} onClick={onClick} />
{text}
</span>
);
function createReactionsIcon(item, updateReaction) {
const { like, dislike, maybe } = item.reactions;
const icons = [
{ key: 'like', text: `${like.count}`, type: 'heart' },
{ key: 'dislike', text: `${dislike.count}`, type: 'dislike' },
{ key: 'maybe', text: `${maybe.count}`, type: 'meh' },
];
return icons.map(({ key, text, type }) => (
<IconText
onClick={() => updateReaction(item.id, key)}
key={key}
type={type}
text={text}
/>
));
}
export default class SingleUpload extends React.Component {
render() {
const { values } = this.props;
return (
<div style={CARD_CONTAINER}>
<List
itemLayout={VERTICAL}
dataSource={values}
renderItem={item => {
const { avatar, description, id, uploader: { image, name } } = item;
return (
<List.Item style={USER_LIST}>
<Card
actions={createReactionsIcon(item, this.updateReaction)}
cover={<img alt={UPLOAD} src={image} />}
extra={<Icon type={MORE} />}
hoverable
key={id}
title={(
<a href="/">
<Avatar src={avatar} style={AVATAR} />
{name}
</a>
)}
type={INNER}
style={CARD_LIST}
>
{description}
</Card>
</List.Item>
);
}}
/>
</div>
);
}
}
Home.js
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import SingleUpload from './SingleUpload';
import ComparisonUpload from './ComparisonUpload';
import { STYLES } from '../constants';
import * as actions from '../actions';
import { getUploads } from '../selectors';
const { CARD_CONTAINER } = STYLES;
class Home extends React.Component {
componentDidMount() {
const { actions: { requestUploadList } } = this.props;
requestUploadList();
}
updateReaction = (id, reaction) => {
const { actions: { updateReaction } } = this.props;
const payload = { id, reaction };
updateReaction(payload);
}
render() {
const { uploads } = this.props;
return (
<div style={CARD_CONTAINER}>
<SingleUpload values={[...uploads.values()]} />
<ComparisonUpload values={[...uploads.values()]} />
</div>
);
}
}
Home.propTypes = {
actions: PropTypes.objectOf(PropTypes.object),
uploads: PropTypes.instanceOf(Map),
};
const mapStateToProps = state => ({
uploads: getUploads(state),
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(actions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Pass your function to component as props,
<SingleUpload values={[...uploads.values()]} updateReaction = {this.updateReaction}/>
Now you can use this in your child component,
<IconText onClick={() => this.props.updateReaction(item.id, key)}
You can pass the updateReaction from your parent to child as a callback
<SingleUpload values={[...uploads.values()]} hanldeReaction={this.updateReaction} />
And you can access it in the child using props.hanldeReaction
<Card actions={createReactionsIcon(item, this.props.hanldeReaction)}
You have to pass down the updateReaction() event-handler you defined in Home as a prop to SingleUpload. Then you can access that prop from anywhere inside your component.
Which means we can cleanup the actions prop inside the Card since we only need to pass the item now.
<Card actions={createReactionsIcon(item)}
As well as createReactionsIcon, now we just call that prop directly inside the function
function createReactionsIcon(item) {
const { like, dislike, maybe } = item.reactions;
const icons = [
{ key: 'like', text: `${like.count}`, type: 'heart' },
{ key: 'dislike', text: `${dislike.count}`, type: 'dislike' },
{ key: 'maybe', text: `${maybe.count}`, type: 'meh' },
];
return icons.map(({ key, text, type }) => (
<IconText
onClick={() => this.props.updateReaction(item.id, key)}
key={key}
type={type}
text={text}
/>
));
}
Less redundant code overall which sounds like what you are trying to achieve.

How to access main class function from material UI button component?

I want to call function handleKeyPress() from ToDo.js file in AddButton.js file to set action onClick() for a button from Material UI. May be I should set onClick action for button in some different way ? But any types of import/export declarations dos not work for me.
I want to understand some functions access rules, I'm newbie in JS )
Please tell me what is wrong here.
AddButton.js
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import AddIcon from '#material-ui/icons/Add';
import {handleKey} from '../ToDo';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
},
});
function FloatingActionButtons(props) {
const { classes } = props;
return (
<div>
<Button variant="fab" color="primary" aria-label="Add" className={classes.button} onClick={() => (handleKey)}>
<AddIcon />
</Button>
</div>
);
}
FloatingActionButtons.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(FloatingActionButtons);
ToDo.js
import React, {Component} from 'react';
import './ToDo.css';
import ToDoItem from './components/ToDoItem';
import AppBar from './components/AppBar';
import AddButton from './components/AddButton';
import Logo from './assets/logo.png';
const appBar = <AppBar />
const addButton = <AddButton />
class ToDo extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{
title: 'Cup cleaning',
todo: "Wash and take away the Kurzhiy's cup from WC"
},
{
title: 'Smoking rollton',
todo: 'Do some rollton and cigarettes'
},
{
title: 'Curious dream',
todo: 'Build a time machine'
}
],
title: '',
todo: ''
};
};
createNewToDoItem = () => {
this.setState(({ list, title, todo }) => ({
list: [
...list,
{
title,
todo
}
],
title: '',
todo: ''
}));
};
handleKeyPress = e => {
if (e.target.value !== '') {
if (e.key === 'Enter') {
this.createNewToDoItem();
}
}
};
handleTitleInput = e => {
this.setState({
title: e.target.value,
});
};
handleTodoInput = e => {
this.setState({
todo: e.target.value
});
};
deleteItem = indexToDelete => {
this.setState(({ list }) => ({
list: list.filter((toDo, index) => index !== indexToDelete)
}));
};
editItem = (i, updTitle, updToDo) => {
let arr = this.state.list;
arr[i].title = updTitle;
arr[i].todo = updToDo;
this.setState ({list: arr});
};
eachToDo = (item, i) => {
return <ToDoItem
key={i}
title={item.title}
todo={item.todo}
deleteItem={this.deleteItem.bind(this, i)}
editItem={this.editItem.bind(this, i)}
/>
};
render() {
return (
<div className="ToDo">
<img className="Logo" src={Logo} alt="React logo"/>
<h1 className="ToDo-Header">{appBar}</h1>
<div className="ToDo-Container">
<div className="ToDo-Content">
{this.state.list.map(this.eachToDo)}
</div>
<div>
<input type="text" placeholder="Enter new title" value={this.state.title} onChange={this.handleTitleInput} onKeyPress={this.handleKeyPress}/>
<input type="text" placeholder="Enter new todo" value={this.state.todo} onChange={this.handleTodoInput} onKeyPress={this.handleKeyPress}/>
<button className="ToDo-Add" onClick={this.createNewToDoItem}>+</button>
<p>{addButton}</p>
</div>
</div>
</div>
);
}
}
export default ToDo;
export const handleKey = this.handleKeyPress;
console.log(handleKey)
You refer with this to a property or a method not to a variable, so you shouldn't be using this here at all.
Secondly you will have to import handleKeyPress method to your file first to be able to access it, are you sure that the method is in const { classes } = props; here ? and if yes you should then point the onclick={handleKeyPress()} just to one function it is not necessary to create an arrow function which returns a function { classes } and not to the props, then it should be working

Categories