I'm trying to make a webpage with next.js, and I'm having some problems with the search function.If I remove the line (index)selectedTab === 'tab', my search works fine, whenever I enter text in the search field, it excludes data that does not contain that text, but if I add selectedTab === 'tab' line, this function will disappear. In my test, when I search with selectedTab === 'tab', the onInputEvent and filter functions in the search code will not be used When it comes to execution, I don't quite understand why this happens. I hope you guys can help me out, please.
index.tsx :
import Head from 'next/head'
import Image from 'next/image'
import Script from 'next/script'
import Link from 'next/link'
import { Inter } from '#next/font/google'
import styles from '#/styles/Home.module.css'
import React, { useState } from 'react';
import {useEffect} from "react";//++
//import '#/component/category-context';//??
import { Console } from 'console'
import courseData from "#/course_data/index.json";
export default function HomePage() {//++
const [selectedTab, setSelectedTab] = useState('tab1');
return (
<div>
<Head>
<div className={styles.header}>
<h2>
<Link href="/">元智課程網</Link>
<input type="search" id={styles.search} className="light-table-filter" data-table="order-table" placeholder="請輸入課名、老師、系所" />
<Link href='/' id={styles.searchBtn}><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" className="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/></svg>
</Link>
</h2>
</div>
<p id={styles.loginNav}>登入
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" className="bi bi-person-circle" viewBox="0 0 16 16">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fillRule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
</p>
</Head>
<div>
<ul style={{ display: 'flex' }}>
<li className={styles.bt}
style={{
background: selectedTab === 'tab1' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none'
}}
onClick={() => setSelectedTab('tab1')}
>
工程學院
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab2' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab2')}
>
管理學院
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab3' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab3')}
>
人文社會學院
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab4' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab4')}
>
資訊學院
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab5' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab5')}
>
電機通訊學院
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab6' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab6')}
>
醫護學院
</li>
</ul>
<hr color='lightblue'></hr>
<ul style={{ display: 'flex' }}>
<li className={styles.bt}
style={{
background: selectedTab === 'tab7' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab7')}
>
通識教學部
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab8' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab8')}
>
軍訓室
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab9' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab9')}
>
體育室
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab10' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab10')}
>
國際語言文化中心
</li>
<li className={styles.bt}
style={{
background: selectedTab === 'tab11' ? 'lightblue' : 'white',
textAlign: 'center',
listStyle: 'none',
}}
onClick={() => setSelectedTab('tab11')}
>
全球事務處
</li>
</ul>
<h1><br/></h1>
<table className="order-table">
<tbody>
{selectedTab === 'tab1' && <div>
{courseData["300"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab2' && <div>
{courseData["505"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab3' && <div>
{courseData["600"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab4' && <div>
{courseData["304"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab5' && <div>
{courseData["310"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab6' && <div>
{courseData["A21"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab7' && <div>
{courseData["901"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab8' && <div>
{courseData["903"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab9' && <div>
{courseData["904"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab10' && <div>
{courseData["906"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
{selectedTab === 'tab11' && <div>
{courseData["907"].map((course, index)=>(
<tr key={index}>
<td>{course.courseName}<br/></td>
<td>{course.courseYear}<br/></td>
<td>老師:{course.courseTeacher}</td>
</tr>))}
</div>}
</tbody>
</table>
</div>
</div>
);
}
category-context.js(search) :
if(typeof window === 'object'){
(function(document) {
'use strict';
// 建立 LightTableFilter
var LightTableFilter = (function(Arr) {
var _input;
// 資料輸入事件處理函數
function _onInputEvent(e) {
console.log("input");
_input = e.target;
var tables = document.getElementsByClassName(_input.getAttribute('data-table'));
Arr.forEach.call(tables, function(table) {
Arr.forEach.call(table.tBodies, function(tbody) {
Arr.forEach.call(tbody.rows, _filter);
});
});
}
// 資料篩選函數,顯示包含關鍵字的列,其餘隱藏
function _filter(row) {
console.log("display");
var text = row.textContent.toLowerCase(), val = _input.value.toLowerCase();
row.style.display = text.indexOf(val) === -1 ? 'none' : 'table-row';
}
return {
// 初始化函數
init: function() {
console.log("init");
var inputs = document.getElementsByClassName('light-table-filter');
Arr.forEach.call(inputs, function(input) {
input.oninput = _onInputEvent;
});
}
};
})(Array.prototype);
// 網頁載入完成後,啟動 LightTableFilter
document.addEventListener('readystatechange', function() {
if (document.readyState === 'complete') {
console.log(6);
LightTableFilter.init();
}
});
})(document);
}
somebody suggested me use state to done it, but I have no idea how to do it.
Related
I am trying to hide the grid item of a certain product and let the others slide in. Right now i am using the display:none property but it hides the item instantaneous. I already have the products filtered and i am checking i want to hide the products that are not filtered using somekind an animation. model:
import React, { useState, useEffect } from "react";
import { Container, Grid, Card, Skeleton, useMediaQuery, Grow, } from "#mui/material";
import Filter from "./Filter";
import Product from "./Product/Product";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { Box } from "#mui/system";
const Products = () => {
const { category } = useParams();
const [sortOption, setSortOption] = useState("name");
const [newProducts, setNewProducts] = useState([]);
const [menu, setMenu] = useState(false);
useEffect(() => {
const selectedSort = sessionStorage.getItem("sortOption") || "name";
setSortOption(selectedSort);
}, []);
const sort = (items, option) => {
switch (option) {
case "name":
return items.sort((a, b) => (a.name > b.name ? 1 : -1));
case "up":
return items.sort((a, b) => (a.price.raw > b.price.raw ? 1 : -1));
case "down":
return items.sort((a, b) => (a.price.raw < b.price.raw ? 1 : -1));
default:
return items.sort((a, b) => (a.name > b.name ? 1 : -1));
}
};
const mobile = useMediaQuery("(max-width:600px)");
const { products, loading } = useSelector((state) => state.products);
let items = products.filter((product) => product.categories[0].slug === category);
let newItems = [];
newProducts.forEach((product) => {
items.forEach((item) => {
if (item.id === product.id) newItems.push(item);
});
});
if (newProducts.length > 0) newItems = sort(newItems, sortOption);
else newItems = sort(items, sortOption);
const renderProducts = () => (
<>
<Container maxWidth="lg" >
<Grid
container
direction="row"
spacing={3}
sx={{
pl: !mobile && menu && "300px",
transition: ".3s",
}}>
{items.map((product) => (
newItems.some(newItem => newItem.id === product.id) ? (
<Grid item xs={12} sm={6} md={4} lg={3} key={product.id} >
<Product product={product} disable={newItems.some(newItem => newItem.id === product.id)} />
</Grid>
) : null
))}
</Grid>
</Container>
</>
);
const loadingView = () => (
<Container maxWidth="lg" >
<Grid
container
direction="row"
spacing={3}
sx={{
pl: !mobile && menu && "300px",
transition: ".3s",
}}>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Skeleton variant="rectangular" width="100%" sx={{ height: ["60vh", "50vh"], mb: 1 }} />
<Box sx={{ p: 2 }}>
<Skeleton variant="text" width="100%" sx={{ height: "20px", mb: 1 }} />
<Card variant="flex" flex="flex">
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", mr: 1 }} />
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", }} />
</Card>
<Skeleton variant="text" width="50%" sx={{ height: "20px" }} />
</Box>
<Skeleton variant="rectangular" width="100%" sx={{ height: "35px" }} />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Skeleton variant="rectangular" width="100%" sx={{ height: ["60vh", "50vh"], mb: 1 }} />
<Box sx={{ p: 2 }}>
<Skeleton variant="text" width="100%" sx={{ height: "20px", mb: 1 }} />
<Card variant="flex" flex="flex">
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", mr: 1 }} />
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", }} />
</Card>
<Skeleton variant="text" width="50%" sx={{ height: "20px" }} />
</Box>
<Skeleton variant="rectangular" width="100%" sx={{ height: "35px" }} />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<Skeleton variant="rectangular" width="100%" sx={{ height: ["60vh", "50vh"], mb: 1 }} />
<Box sx={{ p: 2 }}>
<Skeleton variant="text" width="100%" sx={{ height: "20px", mb: 1 }} />
<Card variant="flex" flex="flex">
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", mr: 1 }} />
<Skeleton variant="rectangular" width="30px" sx={{ height: "10px", }} />
</Card>
<Skeleton variant="text" width="50%" sx={{ height: "20px" }} />
</Box>
<Skeleton variant="rectangular" width="100%" sx={{ height: "35px" }} />
</Grid>
</Grid>
</Container>
);
return <>
<Filter
sortOption={sortOption}
setSortOption={setSortOption}
menu={menu}
setMenu={setMenu}
products={items}
setNewProducts={setNewProducts}
category={category}
/>
{!loading ? renderProducts() : loadingView()}</>;
};
export default Products;
You need to filter the items as you expect by color or size first.
...
const filteredProducts = newItems.filter(newItem => newItem.id === product.id);
...
<Container maxWidth="lg" >
<Grid
container
// You don't need to use direction="row" since it is default props value
spacing={3}
>
{filteredProducts.map((product) => (
<Grid item xs={12} sm={6} md={4} lg={3} key={product.id}>
<Product product={product} disable={newItems.some(newItem => newItem.id === product.id)} />
</Grid>
))}
</Grid>
</Container>
...
If you want to filter the products more accurately, then you could build a customer filter function. Please refer to this.
I have a react component that upon clicking showMore. it will load more comments. The issue im facing is that
View {showMore} More Comments
is not showing the items that are left in the array. Currently there are 7 comments in an array, and if you click show more, it will initially read show 3 more, but when i click again it says show 6 more. when it should be a lesser number than 6. It should be like show 2 more, etc. I'm quite confused on how to go about writing this logic.
What am i doing wrong
CommentList.tsx
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import List from "#material-ui/core/List";
import Typography from "#material-ui/core/Typography";
import CommentItem from "./../commentItem/CommentItem";
import moment from "moment";
import OurLink from "../../../common/OurLink";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import OurModal from "../../../common/OurModal";
.....
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(3);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const the_comments = props.comments.length;
const inc = showMore as any;
const showComments = (e) => {
e.preventDefault();
if (inc + 3 <= the_comments) {
setShowMore(inc + 3);
} else {
setShowMore(the_comments);
}
// setShowLessFlag(true);
};
........
const showMoreComments = () => {
return props.comments
.slice(0, showMore)
.sort((a, b) => a.id - b.id)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ cursor: "pointer", fontSize: "12px", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
</span>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
));
};
console.log(props.comments.slice(0, showMore).length);
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View {showMore} More Comments
</OurSecondaryButton>
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{props.comments
.filter((item, i) => item)
.sort((a, b) => b.id - a.id)
.slice(0, showMore)
.map((comment, i) => (
<div key={i}>
<List style={{ paddingBottom: "20px" }}>
<img alt="gravatar" style={{ margin: "-10px 15px" }} src={comment.author.gravatar} width="30" height="30" />
<Typography style={{ display: "inline-block", fontWeight: 700, padding: "5px 0px" }} variant="h6" align="left">
{Object.entries(props.currentUser).length === 0 ? (
<Fragment>
<span style={{ fontSize: "12px", cursor: "pointer", fontWeight: isBold(comment) }} onClick={handleClickOpen}>
{comment.author.username}
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<OurLink
style={{ fontSize: "12px", fontWeight: isBold(comment) }}
to={{
pathname: `/profile/${comment.author.username}`,
}}
title={comment.author.username}
/>
{comment.userId === props.userId && <span style={{ fontSize: "12px" }}> (OP)</span>}
</Fragment>
)}
</Typography>
<div style={ourStyle}>
<CommentItem comment={comment} user={props.user} postId={props.postId} {...props} />
<Typography style={{ fontSize: "12px" }} variant="body1" align="left">
{moment(comment.createdAt).calendar()}
</Typography>
</div>
</List>
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
You want to show 3 more comments each time, or 1-2 items if there are less than 3 items left. So "View 3 More comments" if there are more than 3 left, or "View 1/2 More Comments" if there are only 1 or 2 left.
Or in other words cap the number of new comments shown at 3:
the minimum value of either 3 or (total number of comments - current shown comments = number of comments left).
View {Math.min(3, the_comments - inc)} More Comments
I have a component that scrolls to view after a comment has been submitted.
const commentSubmit = (e: any, id: number) => {
......
divRef.current.scrollIntoView({ behavior: "smooth" });
};
It scrolls to the comment where <div ref={divRef]></div> is placed which is at the bottom of the most recent comment. The probelem is that if i make a new post, it will scroll into the wrong div element. So its just scrolling at the very bottom. How can i tell ref to scroll to the a specific element based on what post the comment belongs to ?
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
// ignore this divRef in comment list its not being called in the comment list.
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
<div ref={divRef}></div>
postItemContainer.tsx
import React, { Fragment, useState, useCallback, useRef } from "react";
import Avatar from "#material-ui/core/Avatar";
import Button from "#material-ui/core/Button";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import Typography from "#material-ui/core/Typography";
import DeleteOutlineOutlinedIcon from "#material-ui/icons/DeleteOutlineOutlined";
import FavoriteIcon from "#material-ui/icons/Favorite";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
import moment from "moment";
import { toast, ToastContainer } from "react-toastify";
import OurLink from "../../../common/OurLink";
import CommentForm from "../comment/CommentForm";
import CommentList from "../commentList/CommentList";
import OurModal from "../../../common/OurModal";
import "react-toastify/dist/ReactToastify.css";
function PostItemContainer(props: any) {
const [openModal, setOpenModal] = useState(false);
const [openForm, setOpenForm] = useState(false);
const [comment_body, setCommentBody] = useState("");
const [gifUrl, setGifUrl] = useState("");
const divRef = React.useRef<any>();
const writeComment = () => {
// this is the same as this.setState({ openForm: !this.state.open })
setOpenForm(!openForm);
};
const commentChange = (comment) => {
setGifUrl("");
setCommentBody(comment);
};
const selectGif = (e) => {
setGifUrl(e.images.downsized_large.url);
setCommentBody("");
// you wont be able to add text comment with a gif, it will look weird :(
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
const commentSubmit = (e: any, id: number) => {
e.preventDefault();
const formData = {
comment_body,
id,
gifUrl,
};
props.postComment(formData);
setCommentBody("");
setOpenForm(false);
console.log(divRef);
divRef.current.scrollIntoView({ behavior: "smooth" });
};
const { post, currentUser, getNotifications } = props;
console.log(divRef);
return (
<Fragment>
{getNotifications && <ToastContainer autoClose={1000} position={toast.POSITION.BOTTOM_RIGHT} />}
<Grid item={true} sm={12} md={12} style={{ margin: "20px 0px" }}>
<Paper style={{ padding: "20px" }}>
<Typography variant="h5" align="left">
<OurLink
style={{ fontSize: "16px" }}
to={{
pathname: `/post/${post.id}`,
state: { post },
}}
title={post.title}
/>
</Typography>
<Grid item={true} sm={12} md={12} style={{ padding: "30px 0px" }}>
<Typography align="left">{post.postContent.slice(0, 50)}</Typography>
</Grid>
<Avatar
style={{
display: "inline-block",
margin: "-10px -20px",
padding: "0px 30px 0px 20px",
}}
sizes="small"
src={post.author.gravatar}
/>
<Typography display="inline" variant="subtitle1" align="left">
<OurLink
to={{
pathname: `/profile/${post.author.username}`,
state: { post },
}}
title={post.author.username}
/>
</Typography>
<Typography align="right">Likes: {post.likeCounts}</Typography>
<Grid container={true} spacing={1} style={{ padding: "20px 0px" }}>
<Grid item={true} sm={10} lg={10} md={10} style={{ padding: "0px 0px" }}>
<Typography align="left">
{currentUser && currentUser.user && post.userId === currentUser.user.id ? (
<span style={{ cursor: "pointer" }} onClick={() => props.deletePost(post.id, post.userId)}>
<DeleteOutlineOutlinedIcon style={{ margin: "-5px 0px" }} color="primary" /> <span>Delete</span>
</span>
) : null}
</Typography>
</Grid>
<Grid item={true} sm={2} lg={2} style={{ padding: "0px 15px" }}>
<Typography align="right">
{Object.entries(currentUser).length === 0 ? (
<Fragment>
<span onClick={handleClickOpen}>
<FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
</span>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
{post.likedByMe === true ? (
<span style={{ cursor: "pointer" }} onClick={() => props.dislikePost(post.id)}>
<FavoriteIcon style={{ color: "red" }} />
</span>
) : (
<span onClick={() => props.likePost(post.id)}>
<FavoriteBorderIcon style={{ color: "red", cursor: "pointer" }} />
</span>
)}
</Fragment>
)}
</Typography>
</Grid>
</Grid>
<Typography variant="h6" align="left">
{moment(post.createdAt).calendar()}
</Typography>
<Grid item={true} sm={12} lg={12} style={{ paddingTop: "40px" }}>
{Object.entries(currentUser).length === 0 ? (
<Fragment>
<Button onClick={handleClickOpen} variant="outlined" component="span" color="primary">
{"Write A Comment"}
</Button>
{openModal ? <OurModal open={openModal} handleClose={handleCloseModal} /> : null}
</Fragment>
) : (
<Fragment>
<Button onClick={writeComment} variant="outlined" component="span" color="primary">
{openForm ? "Close" : "Write A Comment"}
</Button>
</Fragment>
)}
{openForm ? (
<CommentForm
commentChange={(e: any) => commentChange(e.target.value)}
comment_body={comment_body}
onSubmit={(e) => commentSubmit(e, post.id)}
gifUrl={selectGif}
isGif={gifUrl}
/>
) : null}
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
<div ref={divRef}></div>
</Grid>
</Paper>
</Grid>
</Fragment>
);
}
export default PostItemContainer
CommentList.tsx(just in case) how the comments are bing mapped and iterated
import React, { Fragment, useState } from "react";
import Grid from "#material-ui/core/Grid";
import OurSecondaryButton from "../../../common/OurSecondaryButton";
import CommentListContainer from "../commentListContainer/commentListContainer";
function CommentList(props: any) {
const [showMore, setShowMore] = useState<Number>(2);
const [openModal, setOpenModal] = useState(false);
const [showLessFlag, setShowLessFlag] = useState<Boolean>(false);
const the_comments = props.comments.length;
const inc = showMore as any;
const min = Math.min(2, the_comments - inc);
const showComments = (e) => {
e.preventDefault();
if (inc + 2 && inc <= the_comments) {
setShowMore(inc + 2);
setShowLessFlag(true);
} else {
setShowMore(the_comments);
}
};
const handleClickOpen = () => {
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
const showLessComments = (e) => {
e.preventDefault();
setShowMore(2);
setShowLessFlag(false);
};
const isBold = (comment) => {
return comment.userId === props.userId ? 800 : 400;
};
// show comments by recent, and have the latest comment at the bottom, with the previous one just before it.
const filterComments = props.comments
.slice(0)
.sort((a, b) => {
const date1 = new Date(a.createdAt) as any;
const date2 = new Date(b.createdAt) as any;
return date2 - date1;
})
.slice(0, inc)
.reverse();
const showMoreComments = () => {
return filterComments.map((comment, i) => (
<div key={i}>
<CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
</div>
));
};
return (
<Grid>
<Fragment>
<div style={{ margin: "30px 0px" }}>
{props.comments.length > 2 ? (
<Fragment>
{min !== -1 && min !== -2 ? (
<Fragment>
{min !== 0 ? (
<OurSecondaryButton onClick={(e) => showComments(e)} component="span" color="secondary">
View {min !== -1 && min !== -2 ? min : 0} More Comments
</OurSecondaryButton>
) : (
<OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
Show Less Comments
</OurSecondaryButton>
)}
</Fragment>
) : (
<OurSecondaryButton onClick={(e) => showLessComments(e)} component="span" color="secondary">
Show Less Comments
</OurSecondaryButton>
)}
</Fragment>
) : null}
</div>
</Fragment>
{showLessFlag === true ? (
// will show most recent comments below
showMoreComments()
) : (
<Fragment>
{/* filter based on first comment */}
{filterComments.map((comment, i) => (
<div key={i}>
<CommentListContainer comment={comment} openModal={openModal} handleCloseModal={handleCloseModal} isBold={isBold} handleClickOpen={handleClickOpen} {...props} />
</div>
))}
</Fragment>
)}
</Grid>
);
}
// prevents un-necesary re renders
export default React.memo(CommentList);
The question seems quite complicated, but i over complicated it.
What i had to do was add this
setTimeout(() => {
divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
}, 1500);
in the commentSubmit method, this will scroll to the recent comment.
const commentSubmit = (e: any, id: number) => {
e.preventDefault();
const formData = {
comment_body,
id,
gifUrl,
};
props.postComment(formData);
setCommentBody("");
setOpenForm(false);
console.log(divRef);
// divRef.current.scrollIntoView({ behavior: "smooth" });
// my attempt to scroll to the lastest comment.
setTimeout(() => {
divRef.current.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
}, 1500);
}
And make some adjustments to the code, if there are no comments divRef will throw an error upon adding a comment, because it is looking for a comment but there aren't any comments, so we will do this if there are comments less than 1 or 2. In my case im using 2 cause im showing 2 comments initially and i have a filter that shows more comments, and show less.
{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
reference scrollIntoView Scrolls just too far
Rest of the code
{post.Comments.length === 0 ? <div ref={divRef}></div> : null}
{post.Comments.length > 0 ? (
<Fragment>
<Typography style={{ padding: "10px 0px", margin: "20px 0px" }}>Commments</Typography>
<CommentList ref={divRef} user={currentUser} deleteComment={props.deleteComment} userId={post.userId} postId={post.id} comments={post.Comments} {...props} />
{/* if show more hide show more button and show show less comments button */}
</Fragment>
) : (
<Grid item={true} sm={12} lg={12} style={{ padding: "30px 0px" }}>
<Typography>No Commments Yet</Typography>
</Grid>
)}
{post.Comments.length < 2 ? <div ref={divRef}></div> : null}
I'm new to reactjs and I want an e-trade project. The problem is pressing the first "+" button, but increasing the number in the box next to it. How can I do this?
When I press the first, the other shouldn't increase because I will add more vegetable.
How can I solve this? Thank you so much
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
increment() {
this.setState(prevState => ({
counter: this.state.counter + 1
}));
}
decrement() {
if (this.state.counter === 0) {
this.setState({
counter: 0
});
} else {
this.setState(prevState => ({
counter: this.state.counter - 1
}));
}
}
render() {
return (
<div class="container">
<div class="row" style={{ marginTop: '50px' }}>
<Card className="col-md-3" style={{ }}>
<Card.Img variant="top" src="https://res.cloudinary.com/sivadass/image/upload/v1493620046/dummy-products/broccoli.jpg" style={{ width: '200px',marginLeft:'30px' }}/>
<Card.Body>
<Card.Title style={{ textAlign: 'center' }}> Brocoli - 1
</Card.Title>
<Card.Text style={{ textAlign: 'center' }}>
1 $
</Card.Text>
<div style={{ textAlign: 'center' }}>
<Button onClick={this.decrement.bind(this)}>-</Button>
<h2 value="1">{this.state.counter}</h2>
<Button onClick={this.increment.bind(this)}>+</Button>
</div>
</Card.Body>
</Card>
<Card className="col-md-3" style={{ marginLeft:'10px' }}>
<Card.Img variant="top" src="https://res.cloudinary.com/sivadass/image/upload/v1493620046/dummy-products/broccoli.jpg" style={{ width: '200px',marginLeft:'30px' }}/>
<Card.Body>
<Card.Title style={{ textAlign: 'center' }}> Brocoli - 1
</Card.Title>
<Card.Text style={{ textAlign: 'center' }}>
1 $
</Card.Text>
<div style={{ textAlign: 'center' }}>
<Button onClick={this.decrement.bind(this)}>-</Button>
<h2 value="1">{this.state.counter}</h2>
<Button onClick={this.increment.bind(this)}>+</Button>
</div>
</Card.Body>
</Card>
</div>
</div>
)
}
}
export default App;
Can you please check whether you are looking for the following type of solution or not? Please ignore the styling I have just implemented the functionality.
constructor(props) {
super(props);
this.state = {
items:[{
name:"Veg 1",
counter:0
},{
name:"Veg 2",
counter:0
}]
};
}
increment = (index) =>{
let localItems = [...this.state.items];
localItems[index].counter += 1;
this.setState({
...this.state,
items:[...localItems]
})
}
decrement = (index) =>{
let localItems = [...this.state.items];
localItems[index].counter -= 1;
this.setState({
...this.state,
items:[...localItems]
})
}
render() {
const { classes } = this.props;
return (
this.state.items.length ? this.state.items.map((item,index)=>{
return (
<div>
<button onClick={()=>this.increment(index)}>+</button>
<span>{item.name} - {item.counter}</span>
<button onClick={()=>item.counter && this.decrement(index)}>-</button>
</div>
)
}):null
)
}
Objective
Displaying only the fields that are filled in.
Background
In my application people will first fill out the application which has fields like "early reg fee, early reg date, regular reg fee, regular reg date" and so after they fill out all the information and click "view profile" they will see all the fields whether it's filled out or not.
If the value of one of the fields is null or undefined then it would not show up in the profile.
I was trying to do this and I started of by creating a state in the constructor "this.state {value: ''}"
class CompetitionProfileView extends React.Component {
constructor(props) {
super(props);
this.state {value: ''}
this.getContactCard = this.getContactCard.bind(this);
}
getCompetitionValue(path) {
const value = _.get(this.props.competition, path);
return value ? value : '';
}
getCompetitionDateValue(path) {
const value = _.get(this.props.competition, path);
return value ? value.toDateString() : '';
}
getCompetitionTimeValue(path) {
const value = _.get(this.props.competition, path);
return value ? `${
value.getHours() - 12
}:${value.getMinutes()}` : '';
}
getContactCard(num) {
return
this.getCompetitionValue(`Information.contactFirstName${num}`) ?
<Card key={num} style={{backgroundColor: '#f9f9f9', width: '32%'}} zDepth={2}>
<CardTitle title={`${this.getCompetitionValue(`Information.contactFirstName${num}`)} ${this.getCompetitionValue(`Information.contactFirstName${num}`)}`} subtitle={`${this.getCompetitionValue('Information.contactPosition1')}`} />
<Divider/>
<CardText>
<p style={{display: 'flex', justifyContent: 'center'}}><Phone/>{`${this.getCompetitionValue(`Information.contactFirstName${num}`)}`}</p>
<p style={{display: 'flex', justifyContent: 'center'}}><Email/>{`${this.getCompetitionValue(`Information.contactFirstName${num}`)}`}</p>
</CardText>
</Card>
:
'';
}
render(actions) {
return (
<div>
<div className="profileheader" style={{display: 'flex', flexDirection: 'column'}}>
<Paper className='banner-image' style={{backgroundImage: `url(${this.getCompetitionValue('Resources.boardPicture.url')})`,backgroundSize: 'cover',width: '100%', height: '200px', backgroundPositionY: '20%'}} zDepth={3}>
{/* <br style={{lineHeight: '15'}}/> */}
</Paper>
<Paper className='text-main' style={{textAlign: 'center'}}>
<label>{this.getCompetitionValue('Information.name')}</label>
</Paper>
<Paper className='logo-image' style={{backgroundImage: `url(${this.getCompetitionValue('Resources.competitionLogo.url')})`, backgroundSize: 'cover', width: '100px', height: '100px', marginTop: '-110px', marginLeft: '3%', paddingbottom: '20px'}} zDepth={3}/>
</div>
<hr/>
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<Card style={{backgroundColor: '#f9f9f9', width: '49%'}} zDepth={2}>
<RaisedButton style={{display: 'flex', justifyContent: 'center'}} primary={true} label="Application Packet" onClick={() => window.open(this.getCompetitionValue('Resources.applicationPacket.url'), '_blank')}/>
</Card>
<Card style={{backgroundColor: '#f9f9f9', width: '49%'}} zDepth={2}>
<RaisedButton style={{display: 'flex', justifyContent: 'center'}} primary={true} label="Audition Video Info" onClick={() => window.open(this.getCompetitionValue('Resources.auditionVideoInfo.url'), '_blank')}/>
</Card>
</div>
<br/>
<div className='mainbody' style={{display: 'flex', flexDirection: 'row', justifyContent: 'space-between'}}>
<br/>
<div className='rightbody' style={{display: 'flex', flexDirection: 'column', width: '60%', flexWrap: 'wrap'}}>
<Card style={{backgroundColor: '#F0EFEF'}} zDepth={2}>
<CardHeader title="About Us" />
<Divider/>
<CardText>{`${this.getCompetitionValue('Information.compBlurb')}`}</CardText>
</Card>
<br/>
<Card style={{backgroundColor: '#F0EFEF'}} zDepth={2}>
<CardHeader title="Application Information" />
<Divider/>
<CardText>
<p><b>Early Reg:</b>{` ${this.getCompetitionDateValue('Information.dueDateEarly')}`}</p>
<p><b>Early Reg Fee:</b>{` ${this.getCompetitionValue('Information.earlyDues')}`}</p>
<p><b>Regular Reg:</b>{` ${this.getCompetitionDateValue('Information.dueDateRegular')}`}</p>
<p><b>Regular Reg Fee:</b>{` ${this.getCompetitionValue('Information.regularDues')}`}</p>
<p><b>Late Reg:</b>{` ${this.getCompetitionDateValue('Information.dueDateLate')}`}</p>
<p><b>Late Reg Fee:</b>{` ${this.getCompetitionValue('Information.lateDues')}`}</p>
<p><b>Applications Due At:</b>{` ${this.getCompetitionTimeValue('Information.dueTime')}`}</p>
<p><b>Time Zone:</b>{` ${this.getCompetitionValue('Information.timeZone')}`}</p>
<p><b>Penalties:</b>{` ${this.getCompetitionValue('Information.extraFees')}`}</p>
<p><b>Hear Back Date:</b>{` ${this.getCompetitionDateValue('Information.hearbackDate')}`}</p>
<p><b>Payment Method:</b>{` ${this.getCompetitionValue('Information.paymentMethods')}`}</p>
<br/>
</CardText>
</Card>
</div>
</div>
<br/>
<div className="contactinfo" style={{display: 'flex', justifyContent: 'space-around'}}>
{[1,2,3].map((num) => this.getContactCard(num))}
</div>
<br/>
{this.props.competition.Board.length > 0 &&
<Card style={{backgroundColor: '#F0EFEF'}} zDepth={2}>
<Table >
<TableHeader adjustForCheckbox={false} displaySelectAll={false}>
<TableRow>
{ Object.keys(this.props.competition.Board[0]).map((key) => <TableHeaderColumn key={key}>{key}</TableHeaderColumn>) }
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false}>
{this.props.competition.Board.map((row, i) => (
<TableRow key={i}>
{ Object.keys(row).map((column) => <TableRowColumn key={column}>{row[column].name ? row[column].name : row[column]}</TableRowColumn>) }
</TableRow>
))
}
</TableBody>
</Table>
</Card>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
messages: state.messages
};
};
export default connect(mapStateToProps)(CompetitionProfileView);
You can use if statements in your render() function, like so:
render() {
if (this.props.thing1) {
return <h1>Thing 1</h1>
} else {
return (
<div className="warning">
<h2>Thing 2</h2>
</div>
)
}
}
You can even use functions in the render() function like this:
renderASmallPart() {
if (this.props.thing1) {
return <h1>Thing 1</h1>
} else {
return (
<div className="warning">
<h2>Thing 2</h2>
</div>
)
}
}
render() {
return (
<div>
<h1>My App</h1>
<h2>Here's a thing:</h2>
{this.renderASmallPart()}
</div>
)
}
You can use this to break up your large render() function into smaller functions that check what the value of their field is and only render something when the field has a non-empty value
You need to look into Conditional Rendering and only render that element when there is a value.
https://facebook.github.io/react/docs/conditional-rendering.html
https://atticuswhite.com/blog/render-if-conditionally-render-react-components/
http://devnacho.com/2016/02/15/different-ways-to-add-if-else-statements-in-JSX/
https://kylewbanks.com/blog/how-to-conditionally-render-a-component-in-react-native