I'm new to Redux and React and I'm struggling with an issue for a while now. I have a reducer which contains a favorites array and another array storing the id's of the favorite movies.
Everything seemed to be working at first, but
the problem is when I open the app in another browser I receive error of *cannot read property filter / find of undefined, I get an error everywhere where I try to filter arrays of my reducer. What should I do differently ? I need to filter them because I want my buttons text to be 'Added' for the movies already in the watchlist. Is there something wrong with my reducer or is there another method I could use for filtering?
const intitialState = {
favorites: [],
favIds: [],
btnId: [],
isFavorite: false,
toggle: false,
};
const watchlistReducer = (state = intitialState, action) => {
console.log(action, state);
switch (action.type) {
case 'ADD_WATCHLIST':
return {
...state,
favorites: [...state.favorites, action.payload],
favIds: [
...state.favorites.map((movie) => movie.id),
action.payload.id,
],
btnId: action.payload.id,
isFavorite: true,
toggle: true,
};
case 'REMOVE_WATCHLIST': {
return {
...state,
favorites: [
...state.favorites.filter((movie) => movie.id !== action.payload.id),
],
favIds: [...state.favorites.map((movie) => movie.id)],
btnId: action.payload.id,
isFavorite: false,
toggle: false,
};
}
case 'CLEAR_ALL': {
return intitialState;
}
default:
return state;
}
};
export default watchlistReducer;
const MovieDetail = () => {
const dispatch = useDispatch();
const { movie, isLoading } = useSelector((state) => state.detail);
const { favorites, favIds } = useSelector((state) => state.watchlist);
const [addFav, setAddFav] = useState([favorites]);
//Toggle
const [btnText, setBtnText] = useState('Add to Watchlist');
const [isToggled, setIsToggled] = useLocalStorage('toggled', false);
const [selected, setSelected] = useState([favIds]);
const history = useHistory();
const exitDetailHandler = (e) => {
const element = e.target;
if (element.classList.contains('shadow')) {
document.body.style.overflow = 'auto';
history.push('/');
}
};
const shorten = (text, max) => {
return text && text.length > max
? text.slice(0, max).split(' ').slice(0, -1).join(' ')
: text;
};
const toggleClass = () => {
setIsToggled(!isToggled);
};
const changeText = (id) => {
const check = favIds.filter((id) => id === movie.id);
check.includes(id) ? setBtnText('Added') : setBtnText('Add to Watchlist');
};
const addFavHandler = (movie) => {
const checkMovie = favorites.find((fav) => fav.id === movie.id);
if (!checkMovie) {
dispatch(addWatchlist(movie));
setAddFav([...addFav, movie]);
setBtnText('Added');
} else if (checkMovie) {
alert('Remove from Watchlist?');
dispatch(removeWatchlist(movie));
setBtnText('Add to Watchlist');
} else {
setAddFav([...addFav]);
}
};
return (
<>
{!isLoading && (
<StyledCard>
<StyledDetails
onChange={() => changeText(movie.id)}
className='shadow'
style={{
backgroundImage: ` url("${bgPath}${movie.backdrop_path}")`,
backGroundPosition: 'center center',
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundBlendMode: 'multiply',
}}
onClick={exitDetailHandler}
>
<StyledInfo>
<StyledMedia>
<img
src={
movie.poster_path || movie.backdrop_path
? `${imgPath}${movie.poster_path}`
: unavailable
}
alt='movie'
/>
</StyledMedia>
<StyledDescription>
<div className='genre'>
<h3>{movie.title}</h3>
<p>
{movie.genres &&
movie.genres.map((genre) => (
<span key={genre.id}>{genre.name}</span>
))}
</p>
</div>
<div className='stats'>
<p>
Release Date: <br />{' '}
<span>
{' '}
{movie.release_date ? movie.release_date : ` N / A`}{' '}
</span>
</p>
<p>
Rating: <br /> <span> {movie.vote_average} </span>
</p>
<p>
Runtime: <br /> <span>{movie.runtime} min</span>
</p>
</div>
<div className='synopsys'>
<p>
{shorten(`${movie.overview}`, 260)}
...
</p>
</div>
<button
className={btnText === 'Added' ? 'added' : 'btn'}
id={movie.id}
key={movie.id}
value={movie.id}
type='submit'
onClick={() => {
toggleClass();
addFavHandler(movie);
}}
>
{btnText}
</button>
</StyledDescription>
</StyledInfo>
</StyledDetails>
</StyledCard>
)}
</>
);
};
I created a Django + React Todo App which is working as expected on the frontend but the functionality of updating the backend when a new item/todo is not know to me well. Also, i was able to manage the delete functionality but not the rest (got stuck with handleItem function). Need help to make the add items functionality work :(
Frontend Code below, Backend code is here "https://bitbucket.org/Yash-Marmat/backend/src/master/"
import React, { Component } from "react";
import axios from "axios";
import "bootstrap/dist/css/bootstrap.min.css";
import "font-awesome/css/font-awesome.min.css";
//import "#fortawesome/react-fontawesome";
import "./App.css";
class App extends Component {
constructor() {
super();
this.state = {
newID: 11,
edit: false,
cloneID: 0,
cloneTitle: "",
todoData: [], // will come from django backend
};
this.handleUserInput = this.handleUserInput.bind(this);
this.handleAddItem = this.handleAddItem.bind(this);
this.handleEdits = this.handleEdits.bind(this);
this.renderEdits = this.renderEdits.bind(this);
this.handleUpdates = this.handleUpdates.bind(this);
this.onRemove = this.onRemove.bind(this);
this.handleStrike = this.handleStrike.bind(this);
this.refreshList = this.refreshList.bind(this);
this.getTodos = this.getTodos.bind(this);
this.increment = this.increment.bind(this);
}
// increment id
increment() {
this.setState((prevState) => ({
newID: prevState.newID + 1,
}));
}
// Todo Creation Function (part 1)
handleUserInput(event) {
this.setState({
userInput: event.target.value,
});
}
// PROBLEM BELOW
// Todo Creation Function (part 2)
handleAddItem(id) {
const someID = this.state.newID;
//console.log(someID)
this.setState((prevState) => ({
todoData: [
...prevState.todoData,
{ id: someID, task: this.state.userInput },
],
userInput: "", // im telling react to empty my userInput (the input box)
}));
}
// Todo edit funciton (part 1)
// function 1 (runs the very first time (if edit gets clicked))
handleEdits(theId, theTitle) {
this.setState({
edit: true,
cloneID: theId,
cloneTitle: theTitle,
});
}
// Todo edit function (part 2)
// function 2 (runs automatically after function 1)
// (will run only when the edit condition is true (when we click on edit button))
renderEdits() {
if (this.state.edit) {
return (
<div>
<form onSubmit={this.handleUpdates}>
<input
autoFocus={true}
placeholder="Update Todos"
type="text"
name="data"
defaultValue={this.state.cloneTitle} // from the cloneTitle
className="form-control"
/>
<button
type="submit"
className="btn btn-sm btn-success ml-2 updateButton"
>
Update
</button>
</form>
</div>
);
}
}
// Todo edit Function (part 3)
// function 3 (will run when function 2 runs)
handleUpdates(event) {
event.preventDefault();
this.setState({
todoData: this.state.todoData.map((item) => {
if (item.id === this.state.cloneID) {
item["task"] = event.target.data.value;
return item;
} else {
return item;
}
}),
});
this.setState({
edit: false,
});
}
// Todo delete function
// onRemove(myId) {
// this.setState((prevState) => {
// const updatedList = prevState.todoData.filter((each) => each.id !== myId);
// return {
// todoData: updatedList
// };
// });
// }
onRemove(myId) {
axios
.delete(`http://localhost:8000/api/todos/${myId}`)
.then((res) => this.refreshList());
}
refreshList = () => {
axios
.get("http://localhost:8000/api/todos/")
.then((res) => this.setState({ todoData: res.data }))
.catch((err) => console.log(err));
};
handleStrike(theId, theTask) {
const todoData = this.state.todoData.map((item) => {
if (item.id === theId) {
item["id"] = theId;
item["task"] = theTask;
item["completed"] = !item.completed;
return item;
} else {
return item;
}
});
this.setState({
todoData: todoData,
});
}
// Lifecycle Method
componentDidMount() {
this.getTodos(); // managing the django apis
}
// working with componentDidMount Method
getTodos() {
axios
.get("http://127.0.0.1:8000/api/todos")
.then((res) => {
this.setState({ todoData: res.data });
})
.catch((err) => {
console.log(err);
});
}
render() {
console.log(this.state.todoData)
return (
<div className="card mb-3 sizing mx-auto">
{this.renderEdits()}
{this.state.todoData.map((item) => (
<div className="card-body border text-grey" key={item.id}>
<span className={"crossed-line" + (item.completed ? "" : "active")}>
{item.task}
</span>
{/* edit button below */}
<span
onClick={() => this.handleEdits(item.id, item.task)}
className="shift2 mr-1"
>
<i className="fas fa-edit"></i>
</span>
{/* delete button */}
<span onClick={() => this.onRemove(item.id)} className="mr-2">
<i className="shift fas fa-trash ml-20"></i>
</span>
<span
className="shift3"
onClick={() => this.handleStrike(item.id, item.task)}
>
{item.completed ? (
<i className="fas fa-undo-alt"></i>
) : (
<i className="fas fa-check-circle"></i>
)}
</span>
</div>
))}
<br />
<span>
<input
autoFocus={true}
placeholder="Add Todos"
value={this.state.userInput || ""}
onChange={this.handleUserInput}
className="form-control"
/>
<span
style={{ color: "purple" }}
className="addButton"
onClick={() => {
this.handleAddItem();
this.increment();
}}
disabled={!this.state.userInput}
>
<i className="fas fa-plus-square shift ml-20"></i>
</span>
</span>
</div>
);
}
}
export default App;
I am using Next.js and React and struck on this problem for days. So in Next.js pages i have dynamic page [postid].js, as follow,
import Layout from "../../components/layout";
import { useRouter } from "next/router";
import Singlepost from "../../components/post/singlepost";
export default function Post() {
const router = useRouter();
const postId = router.query.postid
return (
<Layout>
{console.log(router.query.postid)}
<h1>{router.query.postid}</h1>
<p>This is the post content.</p>
<Singlepost postId={postId}/>
</Layout>
);
}
Here I am sending query params as props(postId) to singlepost component.
But in the Singlepost component i am trying use this props inside componentDidMount which then makes api call to get data, but postId shows undefined so api call fails to fetch data.
componentDidMount() {
const postId = this.props.postId;
console.log("postId:", postId);
singlePost(postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
post: data,
likes: data.likes.length,
like: this.checkLike(data.likes),
comments: data.comments
});
}
});
}
So how do i get the prop value in componentDidMount? Or is there any other way should i approach this problem?
also here's my complete singlepost.js for Reference,
import React, { Component } from "react";
import {
singlePost,
remove,
like,
unlike
} from "../../components/post/apiPost";
import Link from "next/link";
import { isAuthenticated } from "../../components/auth";
import DefaultPost from "../../public/images/courses.png";
import Router, { withRouter } from "next/router";
class SinglePost extends Component {
constructor(props) {
super(props);
this.state = {
post: "",
redirectToHome: false,
redirectToBack: false,
redirectToSignin: false,
like: false,
likes: 0,
comments: [],
};
}
checkLike = likes => {
const userId = isAuthenticated() && isAuthenticated().user._id;
let match = likes.indexOf(userId) !== -1;
return match;
};
componentDidMount() {
const postId = this.props.postId;
console.log("postId:", postId);
singlePost(postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
post: data,
likes: data.likes.length,
like: this.checkLike(data.likes),
comments: data.comments
});
}
});
}
updateComments = comments => {
this.setState({ comments });
};
likeToggle = () => {
if (!isAuthenticated()) {
this.setState({ redirectToSignin: true });
return false;
}
let callApi = this.state.like ? unlike : like;
const userId = isAuthenticated().user._id;
const postId = this.state.post._id;
const token = isAuthenticated().token;
callApi(userId, token, postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
like: !this.state.like,
likes: data.likes.length
});
}
});
};
deletePost = () => {
const postId = this.props.quota;
const token = isAuthenticated().token;
remove(postId, token).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({ redirectToBack: true });
}
});
};
deleteConfirmed = () => {
let answer = window.confirm("Are you sure you want to delete your post?");
if (answer) {
this.deletePost();
}
};
renderPost = post => {
console.log(post);
const posterId = post.postedBy ? `/user/${post.postedBy._id}` : "";
const posterName = post.postedBy ? post.postedBy.name : " Unknown";
const { like, likes, comments } = this.state;
return (
<div className="column">
<img
src={`${process.env.REACT_APP_API_URL}/post/photo/${post._id}`}
alt={post.title}
onError={i => (i.target.src = `${DefaultPost}`)}
className="img-thunbnail"
style={{
height: "300px",
width: "100%",
objectFit: "cover"
}}
/>
<button onClick={this.likeToggle}>
<i className="far fa-thumbs-up text-success bg-dark" />
{likes} Like
</button>{" "}
<span className="button is-primary" onClick={() => Router.back()}>
<strong> Back to posts </strong>
</span>
{isAuthenticated().user &&
isAuthenticated().user._id === post.postedBy._id && (
<>
<span className="button is-warning">
<Link href={`/post/edit/${post._id}`}>
<strong> Update Post </strong>
</Link>
</span>
<button
onClick={this.deleteConfirmed}
className="button is-danger"
>
Delete Post
</button>
</>
)}
<div>
{isAuthenticated().user && isAuthenticated().user.role === "admin" && (
<div class="column">
<div className="columns">
<h5 className="column">Admin</h5>
<p>Edit/Delete as an Admin</p>
<span className="button is-warning">
<Link href={`/post/edit/${post._id}`}>
<a> Update Post </a>
</Link>
</span>
<button
onClick={this.deleteConfirmed}
className="button is-raised is-danger"
>
Delete Post
</button>
</div>
</div>
)}
</div>
<div>
<h4 className="raw"> Description: </h4>
<p className="column">{post.body}</p>
</div>
<br />
</div>
);
};
render() {
console.log("Render Quota:", this.props.postId, this.state.ispost);
const { postId } = this.props;
const {
post,
redirectToHome,
redirectToSignin,
redirectToBack
} = this.state;
if (redirectToHome) {
Router.push("/");
} else if (redirectToSignin) {
Router.push("/signin");
} else if (redirectToBack) {
Router.back();
}
return (
<section className="section">
<div className="container">
<h2 className="title">{post.title}</h2>
{!post ? (
<div className="hero">
<h2>Loading...</h2>
</div>
) : (
this.renderPost(post)
)}
</div>
</section>
);
}
}
export default SinglePost;
UPDATE- SOLVED
After Ayèch Hamza suggestion added getInitialProps method and thats exactly what needed.
So In [postid].js,
import React from "react";
import Layout from "../../components/layout";
import SinglePost from "../../components/post/singlePost";
Post.getInitialProps = async ctx => {
const PostId = ctx.query.postid;
return { PostId };
};
function Post({ PostId }) {
return (
<Layout>
<SinglePost PostId={PostId}/>
</Layout>
);
}
export default Post;
and in SinglePost.js,
componentDidMount() {
const postId = this.props.PostId;
singlePost(postId).then(data => {
if (data.error) {
console.log(data.error);
} else {
this.setState({
post: data,
likes: data.likes.length,
like: this.checkLike(data.likes),
comments: data.comments
});
}
});
}
With the help of a user here I was able to make a component, however now I need a variation of that component. The component is an increment / decrement button with some details:
1 - The button is in an array coming from an API
2 - The increment / decrement must follow a rule defined in the bank, that is, a maximum number of items
3 - A footer with the Buy button should appear automatically when one of the items exists
4 - When I click the Buy button, the items will be saved in the localStorage and a new screen will open, called a shopping cart that will contain the same increment / decrement buttons but with the items saved in the localStorage selected in the list and the buttons should increment / decrement the items and clicking the Confirm Purchase button the localStorage must be updated.
Until item 3 and half of item 4 I was able to do, but I'm having problem with the increment / decrement buttons. Below is the code for my components
//Change Quantity Button
import React from 'react';
import './ChooseQuantity.css';
const ChooseQuantity = ({ value, onChange, additionEnabled }) => {
const shouldIncrement = additionEnabled;
const shouldDecrement = value > 0;
const decrement = () => {
if (shouldDecrement) {
onChange(value - 1);
}
}
const increment = () => {
if (shouldIncrement) {
onChange(value + 1);
}
}
const decrementButton = shouldDecrement ? (
<button className="minus" onClick={decrement}>
<i className="fas fa-minus"></i>
</button>
) : <div className="space-button"></div>
const incrementButton = shouldIncrement ? (
<button className='plus' onClick={increment}>
<i className="fas fa-plus"></i>
</button>
) : <div className="space-button"></div>
return (
<div>
{decrementButton}
<span className="qtd">{value}</span>
{incrementButton}
</div>
)
}
ChooseQuantity.defaultProps = {
value: 0,
additionEnabled: true,
}
export default ChooseQuantity
//Lot Component
import React from 'react'
import ChooseQuantity from '../../components/ChooseQuantity/ChooseQuantity.js';
const Lot = ({
ticketName,
ticketPrevenda,
lotUniqueNumber,
ticketUniqueNumber,
name,
lot,
lotType,
lotNumber,
lotPrice,
lotPriceTax,
lotQuantity,
onQuantityChange,
additionEnabled,
maxPurchase,
separator = '/',
quantity,
newValue,
}) => {
const onQuantityChangeInternal = (newValue) => {
onQuantityChange(ticketName, ticketPrevenda, ticketUniqueNumber, lotType, lotNumber, lotUniqueNumber, lotPrice, lotPriceTax, newValue, lotQuantity, maxPurchase)
}
return (
<div className="row">
<div className="col-8">
<h5 className="lot-name">{name}</h5>
<h5 className="lot-name">
{
lotType === 'U' ? 'Unissex ' : ''
}
{
lotType === 'M' ? 'Masculino ' : ''
}
{
lotType === 'F' ? 'Feminino ' : ''
}
({lotNumber}º Lote)
</h5>
<h6 className="lot-price">
R$ {lotPrice.replace('.', ',')} <br />
<small>(R$ {lotPrice.replace('.', ',')} + R$ {lotPriceTax.replace('.', ',')})</small>
</h6>
</div>
<div className="col-4">
<ChooseQuantity
value={lotQuantity}
onChange={onQuantityChangeInternal}
additionEnabled={additionEnabled}
maxPurchase={maxPurchase}
lot={lot}
lotPrice={lotPrice}
lotPriceTax={lotPriceTax}
lotQuantity={lotQuantity}
newValue={newValue}
/>
</div>
</div>
)
}
export default Lot
import React, { Component } from 'react';
import Lot from '../Cart/Lot';
import './Cart.css';
import '../../components/Css/App.css';
import Header from '../../components/Header/Header';
const separator = '/';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {
cart: [],
lot: [],
qtd: 0,
events: null,
banner_app: null,
event_name: null,
priceTotal: null,
quantity: null,
selectedQuantities: {},
maxTotalItems: 0,
}
}
async componentDidMount() {
const cart = JSON.parse(localStorage.getItem('cart'));
await this.setState({ cart: cart });
this.setState({ events: this.state.cart.events });
this.setState({ banner_app: this.state.cart.events.banner_app });
this.setState({ event_name: this.state.cart.events.name });
this.setState({ priceTotal: this.state.cart.total.price });
this.setState({ quantity: this.state.cart.total.totalQuantity });
this.setState({ lot: this.state.cart.events.tickets.lot });
this.setState({ maxTotalItems: this.state.events.max_purchase });
this.setState({ selectedLots: this.state.cart.events.tickets.lot });
}
onQuantityChange = (ticketName, ticketPrevenda, ticketUniqueNumber, lotType, lotNumber, lotUniqueNumber, lotPrice, lotPriceTax, newValue, lotQuantity) => {
// console.log(lotQuantity);
this.setState(prevState => {
this.setState({
selectedQuantities: { ...prevState.selectedQuantities, [`${ticketName}${separator}${ticketPrevenda}${separator}${ticketUniqueNumber}${separator}${lotType}${separator}${lotNumber}${separator}${lotUniqueNumber}${separator}${lotPrice}${separator}${lotPriceTax}`]: newValue },
})
}, () => {
const selectedArray = Object.entries(this.state.selectedQuantities).map(
([key, quantity]) => {
const [ticketName, ticketPrevenda, ticketUniqueNumber, lotType, lotNumber, lotUniqueNumber, lotPrice, lotPriceTax] = key.split(separator)
const totalLotPrice = parseFloat(lotPrice + lotPriceTax);
const total = parseFloat(totalLotPrice * quantity);
return {
ticketName,
ticketPrevenda,
ticketUniqueNumber,
lotType,
lotNumber,
lotUniqueNumber,
lotPrice,
lotPriceTax,
quantity,
totalLotPrice,
total
}
},
)
// console.log(selectedArray);
// console.log(this.state.quantity);
//SOMANDO A QTD E ATUALIZANDO O STATE
// var lotQuantity = selectedArray.reduce(function(prevVal, elem) {
// console.log(elem.quantity)
// const lotQuantity = prevVal + elem.quantity;
// return lotQuantity;
// }, 0);
// this.setState({ qtd: lotQuantity });
// console.log(this.state.lotQuantity);
// //SOMANDO O TOTAL E ATUIALIZANDO O STATE
// var total = selectedArray.reduce(function(prevVal, elem) {
// const total = prevVal + elem.total;
// return total;
// }, 0);
// this.setState({priceTotal: total})
// //MOSTRAR/OCULTAR FOOTER
// if (lotQuantity > 0) {
// this.setState({ totalZero: true });
// } else {
// this.setState({ totalZero: false });
// }
// //OBJETO CART
// var lot = selectedArray;
// var tickets = {
// name: ticketName,
// prevenda: ticketPrevenda,
// unique_number: ticketUniqueNumber,
// lot: lot
// }
// total = {
// price: total,
// quantity: lotQuantity,
// };
// var events = {
// banner_app: this.state.event.banner_app,
// installments: this.state.event.installments,
// max_purchase: this.state.event.max_purchase,
// name: this.state.event.name,
// tickets: tickets,
// unique_number: this.state.event.unique_number,
// total_tickets: lotQuantity
// };
// var cart = { events: events, total: total };
// localStorage.setItem('cart', JSON.stringify(cart));
// localStorage.setItem('qtd', JSON.stringify(lotQuantity));
// console.log(lotQuantity);
// console.log(JSON.parse(localStorage.getItem('cart')));
//OBJETO CART
})
}
// getSelectedQuantity = (ticketName, ticketPrevenda, ticketUniqueNumber, lotType, lotNumber, lotUniqueNumber, lotPrice, lotPriceTax) => this.state.selectedQuantities[`${ticketName}${separator}${ticketPrevenda}${separator}${ticketUniqueNumber}${separator}${lotType}${separator}${lotNumber}${separator}${lotUniqueNumber}${separator}${lotPrice}${separator}${lotPriceTax}`];
//HERE IS THE FUNCTION THAT INCREMENT/DECREMENT ITEMS
getSelectedQuantity = (ticketName, ticketPrevenda, ticketUniqueNumber, lotType, lotNumber, lotUniqueNumber, lotPrice, lotPriceTax, lotQuantity) => {
// console.log(lotQuantity);
// console.log(this.state.selectedQuantities);
// lot.reduce(function(prevVal, elem) {
// // const soma = parseInt(elem) + parseInt(lotQuantity);
// console.log(elem)
// // return soma;
// }, 0);
// console.log(myLotQuantity);
// return myLotQuantity;
// console.log(this.state.selectedQuantities);
return this.state.selectedQuantities[
`${ticketName}${separator}${ticketPrevenda}${separator}${ticketUniqueNumber}${separator}${lotType}${separator}${lotNumber}${separator}${lotUniqueNumber}${separator}${lotPrice}${separator}${lotPriceTax}`
];
// return lotQuantity;
}
getAdditionEnabled = () => Object.values(this.state.selectedQuantities).reduce((acc, i) => acc + i, 0) < this.state.maxTotalItems;
onCheckoutButtonClick = () => {
const selectedArray = Object.entries(this.state.selectedQuantities).map(
([key, quantity]) => {
const [ticketName, ticketPrevenda, ticketUniqueNumber, lotType, lotNumber, lotUniqueNumber, lotPrice, lotPriceTax] = key.split(separator)
const totalLotPrice = parseFloat(lotPrice + lotPriceTax);
const total = parseFloat(totalLotPrice * quantity);
return {
ticketName,
ticketPrevenda,
ticketUniqueNumber,
lotType,
lotNumber,
lotUniqueNumber,
lotPrice,
lotPriceTax,
quantity,
totalLotPrice,
total
}
},
)
console.log(selectedArray);
}
render() {
return (
<div>
<Header Title="Carrinho" ToPage="/" />
<div className="cart">
<div className="container-fluid">
<div className="box-price">
<div className="row box-default ">
<div className="col col-price">
<h6>{this.state.quantity} INGRESSO{this.state.quantity > 1 ? 'S' : ''}</h6>
<h5>R$ {parseFloat(this.state.priceTotal).toFixed(2).replace('.', ',')}</h5>
</div>
</div>
</div>
<div className="box-default">
<div className="row no-margin">
<div className="col-12 col-image no-padding">
<img src={this.state.banner_app} alt="" />
</div>
<div className="col-12 no-padding">
<h5 className="event-name">{this.state.event_name}</h5>
</div>
</div>
{
this.state.lot.map((lot, l) =>
// <div className="row" key={l}>
// <div className="col-8">
// <h5 className="lot-name">{lot.name}</h5>
// <h5 className="lot-name">
// {
// lot.lotType === 'U' ? 'Unissex ' : ''
// }
// {
// lot.lotType === 'M' ? 'Masculino ' : ''
// }
// {
// lot.lotType === 'F' ? 'Feminino ' : ''
// }
// ({lot.lotNumber}º Lote)
// </h5>
// <h6 className="lot-price">
// R$ {lot.lotPrice.replace('.', ',')} <br />
// <small>(R$ {lot.lotPrice.replace('.', ',')} + R$ {lot.lotPriceTax.replace('.', ',')})</small>
// </h6>
// </div>
// <div className="col-4">
// <ChooseQuantity
// value={this.getSelectedQuantity(lot.quantity)}
// onChange={this.onQuantityChange}
// additionEnabled={this.additionEnabled}
// maxPurchase={this.state.events.max_purchase}
// lotPrice={lot.lotPrice}
// lotPriceTax={lot.lotPriceTax}
// lotQuantity={lot.lotQuantity}
// />
// </div>
// <div className="col-12">
// {this.state.lot.length > 1 ? <hr /> : ''}
// </div>
// </div>
<div key={l}>
<Lot
events={this.state.events}
ticketName={this.state.cart.events.tickets.name}
ticketPrevenda={this.state.cart.events.tickets.prevenda}
ticketUniqueNumber={this.state.cart.events.tickets.unique_number}
lotUniqueNumber={lot.lotUniqueNumber}
lotName={lot.name}
lotType={lot.lotType}
lotNumber={lot.lotNumber}
lotPrice={lot.lotPrice}
lotPriceTax={lot.lotPriceTax}
onQuantityChange={this.onQuantityChange}
maxPurchase={this.state.events.max_purchase}
lotQuantity={this.getSelectedQuantity(this.state.cart.events.tickets.name, this.state.cart.events.tickets.prevenda, this.state.cart.events.tickets.unique_number, lot.lotType, lot.lotNumber, lot.lotUniqueNumber, lot.lotPrice, lot.lotPriceTax, lot.quantity)}
// quantity={lot.quantity}
additionEnabled={this.getAdditionEnabled()}
/>
{this.state.lot.length > 1 ? <hr /> : ''}
</div>
)
}
<div className="row cart-footer" style={{ marginRight: '-15px', marginLeft: '-15px', backgroundColor: '#f4f7fa' }}>
<button className="col col-purchase" to="/cart" style={{ justifyContent: 'center', alignItems: 'center' }} onClick={this.onCheckoutButtonClick}>
Confirmar Comprar
</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Cart;
In the getSelectedQuantity function if I put
return this.state.selectedQuantities[
`${ticketName}${separator}${ticketPrevenda}${separator}${ticketUniqueNumber}${separator}${lotType}${separator}${lotNumber}${separator}${lotUniqueNumber}${separator}${lotPrice}${separator}${lotPriceTax}`
];
It updates the items on the increment / decrement button, but I need it to show the quantity of items that exist in the localStorage and update the quantity. I've been trying for a few days and nothing
This might not be a complete answer for your problem, but you're causing a bloody hell in your componentDidMount, especially by calling this.setState() so many times. Remember a thing - each time you call this.setState you're re-rendering the component.
Also, this.setState() doesn't return a promise, so await this.setState({...}) doesn't make any sense.
So, the first improvement for your component will be:
componentDidMount() {
const cart = JSON.parse(localStorage.getItem('cart'));
const {
events,
events: { tickets },
total
} = cart;
this.setState({
cart,
events,
banner_app: events.banner_app,
event_name: events.event_name,
priceTotal: total.price,
quantity: total.totalQuantity,
lot: tickets.lot,
maxTotalItems: events.max_purchase,
selectedLots: tickets.lot,
});
}
My search form is dynmically created using a ajax call for the inputs. Each input can then be used alone or in combination with other inputs to narrow down the search results. The problem I am having is that the submit method is running a new search each time an additional input is added to the form. For example: User just searches with one input. Submit method runs once. User searches with two inputs. Search runs once for the single input and then another time for the two inputs. And so on...
Here is my parent file..
class SearchPage extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
labels: [],
contracts: [],
formValues:[],
pdfs:[],
titles:[],
alertShow: false,
show: false,
};
this.onClick = this.handleContract.bind(this);
this.handleShow = this.handleShow.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleShowAlert = this.handleShowAlert.bind(this);
this.handleCloseAlert = this.handleCloseAlert.bind(this)
}
initialState = {
formValues: {},
}
state = this.initialState
componentDidMount(){
this.loadLabels();
}
componentWillUnmount(){
}
toggleHidden () {
this.setState({
isHidden: !this.state.isHidden
})
}
handleFormReset = () => {
this.setState(() => this.initialState)
this.setState({contracts:[]})
}
handleClose() {
this.setState({ show: false });
}
handleShow() {
this.setState({ show: true });
}
handleCloseAlert() {
this.setState({ alertShow: false });
}
handleShowAlert() {
this.setState({ alertShow: true });
}
loadLabels = () => {
API.getLabels()
.then(res => {
const labels = res.data;
this.setState({ labels })
})
.catch(err => console.log(err));
};
handleInputChange = (key, value) => {
const newFormValues = Object.assign({}, this.state.formValues, {[key]: value});
this.setState({ formValues: newFormValues })
};
handleContract = (id) => {
API.openRow(id)
.then(res => {
const pdfs = res.data;
this.setState({pdfs});
this.props.history.push({
state: { labels:this.state.labels,
pdfs:this.state.pdfs,
titles:this.state.titles }
})
})
.catch(err => console.log(err));
API.titles(id)
.then(res => {
const titles = res.data;
this.setState({titles});
})
this.setState({ show: true });
}
handleFormSubmit = event => {
event.preventDefault();
const formData = this.state.formValues
let query = '';
let keys = Object.keys(formData);
keys.map(k => {
if (query !== "")
query += `&`;
query += `filter=`
query += `${k}|${formData[k]}`
return this.loadContracts(query);
})
};
noResults() {
this.setState({alertShow:true})
}
loadContracts = (query) => {
API.search(query)
.then(res => {
const contracts = res.data;
if (contracts.length > 0 ){
this.setState({ contracts });
}
else {
this.noResults();
this.setState({contracts:[]});
};
})
.catch(err => console.log(err));
};
render() {
return (
<div className="container" style={{ marginTop: "80px" }}>
<div className="jumbotron">
<div className="container">
<h1>Contract Document Search</h1>
</div>
<br/>
<Container>
<SearchForm
labels={this.state.labels}
handleFormSubmit={this.handleFormSubmit}
handleInputChange={this.handleInputChange}
handleReset={this.handleReset}
handleFormReset={this.handleFormReset}
/>
<div className='modal'>
<Modal show={this.state.alertShow}
onHide={this.handleCloseAlert}
{...this.props}
size="sm"
aria-labelledby="contained-modal-title-vcenter"
centered>
<Modal.Header closeButton>
<Modal.Body>No results found</Modal.Body>
</Modal.Header>
</Modal>
</div>
</Container>
</div>
<div className="container">
<div className="jumbotron-fluid">
</div>
<SearchResults
labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs}
handleContract={this.onClick}
handleTitles={this.onClick}
/>
<br/>
<br/>
</div>
);
}
}
export default SearchPage;
And My search form component..
export default class SearchForm extends Component {
constructor(...args) {
super(...args);
this.state = {
};
}
render() {
return (
<form className="form-inline col-md-12" onReset={this.props.handleFormReset}>
{this.props.labels.map(label => (
<div className="card border-0 mx-auto" style={styles} key={label.Id}>
<ul className="list-inline ">
<span>
<li>
<Labels htmlFor={label.DisplayName} >{label.DisplayName}:</Labels>
</li>
<li >
<Input
key={label.Id}
onChange={(event) => {
this.props.handleInputChange(label.DataField, event.target.value)}}
value={this.props.newFormValues}
maxLength="999"
style={{height:34}}
name="value"
type="search"
className={"form-control mb-2 mr-sm-2"}
id={label.DataField}
/>
</li>
</span>
</ul>
</div>
))}
<div className=" col-sm-12">
<Button
style={{ float: "left", marginBottom: 10 }}
className="btn btn-success"
type="submit"
onClick={this.props.handleFormSubmit}
>
Search
</Button>
<Help />
<Button
style={{ float: "left", marginBottom: 10 }}
className="btn btn-secondary"
type="reset"
onClick={this.props.handleFormReset}
>
Reset
</Button>
</div>
</form>
);
}
}
The problem was that I had my return statement inside the input mapping.
handleFormSubmit = event => {
event.preventDefault();
const formData = this.state.formValues
let query = '';
let keys = Object.keys(formData);
keys.map(k => {
if (query !== "")
query += `&`;
query += `filter=`
query += `${k}|${formData[k]}`
**return this.loadContracts(query);**
})
};
Solved by moving the return statement outside the mapping.
handleFormSubmit = event => {
event.preventDefault();
const formData = this.state.formValues
let query = '';
let keys = Object.keys(formData);
keys.map(k => {
if (query !== "")
query += `&`;
query += `filter=`
query += `${k}|${formData[k]}`
})
**return this.loadContracts(query);**
};