I have a listing of articles that are displayed on cards.
I need to implement a search bar to search for articles.
In the code I make a map in the CardArticle component so that it can be rendered as the back-end sends the information.
In the back-end there is already a route to search: /v1/articles?search=${search}
When the user accesses the article page, he will display all articles and only when he clicks to search will the search be made. And when he deletes the words from the search bar, he will return to displaying all articles.
code:
export default function Articles() {
const { data: articles } = useSWR(`/v1/articles`, fetch);
if (!articles) {
return (
<div style={{ paddingTop: 90 }}>
<Loading />
</div>
);
}
return (
<>
<Search>
<span>
<IconSearch color={theme.colorsCommon.secundary} />
</span>
<input placeholder="Busque por autor ou artigos" />
</Search>
{articles.map(article => (
<Link to={`/articles/${article.slug}`}>
<CardArticle key={article.guid}>
<Image>
<img
src={!article ? noPhoto : verifyPhoto(article.cover_photo)}
alt={article.title}
/>
</Image>
<TopCard>
<div className="categorys">
{article.categories.map(category => (
<Category key={category.id}>{category.name}</Category>
))}
</div>
</TopCard>
<DetailsArticle>
<div className="title">
<span>{article.title}</span>
</div>
</DetailsArticle>
<BottomCard>
<div className="author">
<img
src={
!article.author
? noPhoto
: verifyPhoto(article.author.photo)
}
alt={!article.author ? [] : article.author.name}
/>
<span>{!article.author ? [] : article.author.name}</span>
</div>
<div className="createDate">{formatDate(article.created_at)}</div>
</BottomCard>
</CardArticle>
</Link>
))}
</>
);
}
export default function Articles() {
const [search, setSearch] = useState('');
const [debounceSearch, setdebounceSearch] = useState('');
const { data: articles } = useSWR(
`/v1/articles${debounceSearch ? `?search=${debounceSearch}` : ''}`,
fetch
);
const handleOnChange = useCallback(({ target: { value } }) => {
setSearch(value);
}, []);
useEffect(() => {
const timerId = setTimeout(() => {
setdebounceSearch(search);
}, 250);
return () => {
clearTimeout(timerId);
};
}, [search]);
if (!articles) {
return (
<div style={{ paddingTop: 90 }}>
<Loading />
</div>
);
}
return (
<>
<Search>
<span>
<IconSearch color={theme.colorsCommon.secundary} />
</span>
<input
value={search}
placeholder="Busque por autor ou artigos"
onChange={handleOnChange}
/>
</Search>
{articles.map(article => (
<Link to={`/articles/${article.slug}`}>
<CardArticle key={article.guid}>
<Image>
<img
src={!article ? noPhoto : verifyPhoto(article.cover_photo)}
alt={article.title}
/>
</Image>
<TopCard>
<div className="categorys">
{article.categories.map(category => (
<Category key={category.id}>{category.name}</Category>
))}
</div>
</TopCard>
<DetailsArticle>
<div className="title">
<span>{article.title}</span>
</div>
</DetailsArticle>
<BottomCard>
<div className="author">
<img
src={
!article.author
? noPhoto
: verifyPhoto(article.author.photo)
}
alt={!article.author ? [] : article.author.name}
/>
<span>{!article.author ? [] : article.author.name}</span>
</div>
<div className="createDate">{formatDate(article.created_at)}</div>
</BottomCard>
</CardArticle>
</Link>
))}
</>
);
}
Related
i am tryin to route to localhost/detiled on click on the <li>
when i type in url localhost/detiled my <InnerDetail /> is loading i want the same when i click on the <li> tag
and also how can i access suggestion.id in <InnerDetail />
search.js
<Link to={{ pathname:"/detiled" }}
>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
onClick={finddoctor(suggestion.id)}
>
{suggestion.firstname}
</li>
</Link>
in my path.js i have this
path.js
<Route path="/detiled">
<InnerDetail />
</Route>
import PersonPhoto from "../img/doctor.png";
import { useLocation } from "react-router-dom";
import axios from "axios";
import React, { useState, useEffect } from "react";
export default function Detail(props) {
const location = useLocation();
const [detail, setDetail] = useState(false);
//const data3 = location.state.data;
//const doctor_id = data3.id;
const inipath = window.location.pathname;
const path = inipath.split("/cdoctor/");
const [data3, setdata3] = useState([]);
console.log(path[1]);
useEffect(() => {
const config = {
headers: {
Authorization: `token ` + localStorage.getItem("token"),
},
};
//remove this date after setting up the admin pannel
axios
.get(
"doctor-filter/?id=" + path[1],
config
// config
)
.then((res) => {
console.log(res.data);
// setdata3(res.data);
});
});
return (
<>
{/* {detail ? (
<InnerDetail />
) : (
<> */}
<h4 style={{ textAlign: "left", marginLeft: "10px" }}>
Top Specialities <i className="fa fa-angle-right"></i> /doctor/Detail
</h4>
<hr
style={{
margin: "30px 10px",
background: "#fff",
height: "1px",
border: "none",
}}
/>
<div style={styles.wrapper}>
{/* begin header */}
<div style={styles.pheader}>
<div>
<div style={styles.pname}>
<strong style={styles.namealigner}>Dr {data3.firstname}</strong>
<br />
<strong style={styles.namealigner}> {data3.lastname}</strong>
</div>
<hr style={styles.hr} />
<div style={{ textAlign: "left", fontSize: "12px" }}>
<span>
{" "}
{data3.speciality} | {data3.experience} years of Experience
</span>
</div>
<hr style={styles.hr} />
</div>
<div>
<img style={{ height: "100px" }} src={PersonPhoto} alt="" />
</div>
</div>
{/* end header */}
{/* begin detail */}
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-graduation-cap"></i>
<strong> Education</strong>
<br />
<small> {data3.qualification}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-map-marker"></i>
<strong> Location</strong>
<br />
<small>{data3.location}</small>
</div>
</div>
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-user"></i>
<strong> Registeration Number</strong>
<br />
<small>{data3.councilRegNo}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-globe"></i>
<strong> Language</strong>
<br />
<small>English Malayalam</small>
</div>
</div>
{/* end detail */}
</div>
</>
// )}
// </>
);
}
this is the dashbord here serch is common in all pages
<Search />
<Specialities />
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
function finddoctor(e) {
console.log(e);
setDetail(true);
}
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
//console.log(strung.substring(1, strung.length - 1));
// console.log(JSON.stringify(suggestions));
// var suggestions = suggestions.substring(1, suggestions.length - 1);
// newObj = suggestions;
//console.log(suggestions);
//setFilteredSuggestions({ ...poName, idAddProducts: idAddProducts });
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
// const filteredSuggestions = suggestions.filter((suggestion) =>
// suggestion.toString().toLowerCase().includes(value.toLowerCase())
// );
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this isthe link
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
onClick={() => this.nextPath("/detiled")}
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
You can add a route for detail view of the suggestion.
<Switch>
<Route path="/detailed" exact>
list suggestions component
</Route>
<Route path="/detailed/:id" exact}>
DetailComponent
</Route>
</Switch>
Then the link will become:
<Link to={`/detailed/${suggestion.id}`}>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
And in DetailComponent you can get the id from route params.
import { useParams } from "react-router-dom";
...
const { id } = useParams(); // the id passed in url
I'm trying to create a component that allows a video to autoplay on mouseenter and pauses on mouseleave. However, the current code causes all videos to autoplay when you put the mouseover any single one of the videos. How can I only make the video that you're interacting with update its state in a more isolated way?
I can't seem to find a solution using React hooks anywhere, that I can understand and implement into my own code.
export default function VideoGrid(props) {
const [playing, setPlaying] = useState(false);
return (
<div>
<div className={styles.VideoGrid}>
<div className="container">
<h2 className={styles.title + " text-lg uppercase title"}>{props.title}</h2>
<div className={styles.videos}>
{props.videos ? videos.output.map((video, index) => {
return (
<div className={styles.video} key={index}>
{ video.acf.video_url ? (
<ReactPlayer
controls={false}
playing={playing}
onMouseEnter={() => setPlaying(true)}
onMouseLeave={() => setPlaying(false)}
height={205}
url={video.acf.video_url + '&showinfo=0&controls=0&autohide=1'}
width='100%'
config= {{
youtube: {
playerVars: {showinfo: 0, controls: 0}
}
}}
/>
) : (
<img src={video._embedded ? video._embedded['wp:featuredmedia'][0].media_details.sizes.full.source_url : '/logo.svg'} height={205} />
)}
<p className="mt-2">{video.title.rendered}</p>
{video.acf.description && router.pathname != '/' ? <p className={styles.description + " text-xs"}>{video.acf.description}</p> : ''}
</div>
)
}) : ''}
</div>
</div>
</div>
</div>
)
}
You can create a separate component and deal with the state individually.
const Video = (props) => {
const [playing, setPlaying] = useState(false);
return (
<div className={styles.video} key={index}>
{video.acf.video_url ? (
<ReactPlayer
controls={false}
playing={playing}
onMouseEnter={() => setPlaying(true)}
onMouseLeave={() => setPlaying(false)}
height={205}
url={video.acf.video_url + "&showinfo=0&controls=0&autohide=1"}
width="100%"
config={{
youtube: {
playerVars: { showinfo: 0, controls: 0 },
},
}}
/>
) : (
<img
src={
video._embedded
? video._embedded["wp:featuredmedia"][0].media_details.sizes.full
.source_url
: "/logo.svg"
}
height={205}
/>
)}
<p className="mt-2">{video.title.rendered}</p>
{video.acf.description && router.pathname != "/" ? (
<p className={styles.description + " text-xs"}>
{video.acf.description}
</p>
) : (
""
)}
</div>
);
};
export default Video;
Then render it in your map. You need to do the proper changes to pass your data into the video component.
export default function VideoGrid(props) {
return (
<div>
<div className={styles.VideoGrid}>
<div className="container">
<h2 className={styles.title + " text-lg uppercase title"}>
{props.title}
</h2>
<div className={styles.videos}>
{props.videos
? videos.output.map((video, index) => {
return <Video />;
})
: ""}
</div>
</div>
</div>
</div>
);
}
In my News component I have an array of posts:
const cardInfo = [
{
id: 1,
date: "12/07/2020",
title: "Machine Learning",
author: "Alex M.",
text: "text1"
},
{
id: 2,
date: "12/07/2020",
title: "AI",
author: "Alex E.",
text: "text2"
}
]
A renderCard function that renders the array:
renderCard = (card, index) => {
return (
<Row id={index}>
<Col sm="10">
<Card key={index}>
<CardBody>
<CardTitle tag="h5" className="mb-2 text-muted">
{card.title}
</CardTitle>
<CardSubtitle tag="h6" className="mb-2 text-muted">
Written on {card.date} by {card.author}
</CardSubtitle>
<CardText className="mb-2 text-muted">
<ReadMoreReact
text={card.text}
readMoreText="Read more."
min={160}
ideal={200}
/>
</CardText>
<CardSubtitle tag="h6" className="mb-2 text-muted">
Category: {card.category}
</CardSubtitle>{" "}
<Button>Read more.</Button>
</CardBody>
</Card>
</Col>
</Row>
);
};
Return statement that displays the array of posts and also titles of recent posts sorted by date:
return (
<div>
<CareersHeader />
<div className="news">
<h1 className="news__title" id="news-title">
Blog
</h1>
<div className="news__content">
<div className="item1 news__posts">
{cardInfo.map(this.renderCard)}
</div>
<div className="item2 news__recent_posts">
<div>Recents Posts:</div>
<div>
{cardInfo
.sort((a, b) => (a.date > b.date ? 1 : -1))
.map((post, index) => (
<button onClick={what to do} key={index}>
{post.title}
</button>
))}
</div>
</div>
</div>
</div>
</div>
);
Post titles in recent posts list are buttons. When you click on a button, it should open that particular blog post. I have added in App.js this route:
<Route exact path="/news/:id" component={BlogPostDetails} />
BlogPostDetails component
return (
<div>
<CareersHeader />
<div className="news">
<h1 className="news__title" id="news-title">
Blog
</h1>
<div className="news__content">
<div className="item1 news__posts">
<p>Blog post</p>
{cardInfo.map((card) => (
<div key={card.id}>{renderCard}</div>
))}
</div>
</div>
</div>
</div>
);
However, I don't know how to match the id from URL with the id of the post title clicked.
I have tried with useParams() hook, but I was getting errors that I couldn't resolve (invalid hook call). Also, I am wondering if I could just create a function that would be called on a button click, something like this:
const displayPost= (index) => {
const url = "/news/" + index;
window.open(url, "_top");
};
I'm sure there is quite simple way in React to do this, just that I'm still quite new and don't understand the lifecycle. Help appreciated.
Title part.
import { useHistory } from 'react-router-dom'
.
.
.
function MyPage({ ...rest }){
const history = useHistory()
return(
.
.
.
{cardInfo
.sort((a, b) => (a.date > b.date ? 1 : -1))
.map((post, index) => (
<button onClick={() => history.push(post.id)} key={index}>
{post.title}
</button>
))}
.
.
.
}
.
.
.
)
BlogPostDetails component
import { useRouteMatch } from 'react-router-dom'
const DetailPage = ({
...rest
}){
const match = useRouteMatch()
const { id } = match.params
const [ cardInfo, setCardInfo ] = useState([])
const [ loading, setLoading ] = useState(false)
const [ error, setError ] = useState(null)
useEffect(() =>{
setLoading(true)
//fetch your details from API and set Loading, Error
}, [ id ])
return (
<div>
<CareersHeader />
<div className="news">
<h1 className="news__title" id="news-title">
Blog
</h1>
<div className="news__content">
<div className="item1 news__posts">
<p>Blog post</p>
{!loading && cardInfo.map((card) => (
<div key={card.id}>{renderCard}</div>
))}
</div>
</div>
</div>
</div>
);
}
I have an error with the code below
( Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.)
The goal is to add a span on each item excepted the last one.
What is the best way to do that?
const Section = () => {
const [lastItem, setlastItem] = React.useState(false);
// rendu des Sections
const sectionLentgh = Data.sections.length;
const sectionList = Data.sections.map((item, i) => {
// AJout du séparateur
if (sectionLentgh === i + 1) {
setlastItem(false);
} else {
setlastItem(true);
}
console.log(i);
return (
<div>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage:`url(/images/${item.image})` }}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${ lastItem ? styles.separator : '' }`}></span>
</div>
);
})
return (
<>
<div className={styles.sections}>
{sectionList}
</div>
</>
);
};
export default Section;
Just use the length of the array and compare it to the index of the iteration:
const Section = () => {
const sectionLength = Data.sections.length;
const sectionList = Data.sections.map((item, i) => {
const lastItem = i === (sectionLength - 1);
return (
<div>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})` }}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${lastItem ? styles.separator : ""}`}></span>
</div>
);
});
return (
<>
<div className={styles.sections}>{sectionList}</div>
</>
);
};
export default Section;
You enter an infinite loop because you call setlastItem in the map function, which in turn reruns on every render. Since setState triggers a new render, this causes the infinite loop.
What you want to is to put the generation of the sectionList in a useEffect, that reruns only every time the Data.sections changes.
Like this:
const Section = () => {
const [sectionList, setSectionList] = useState([]);
useEffect(() => {
if(!Data.sections|| Data.sections.length < 2){
setSectionList([]);
} else {
setSectionList(Data.sections.splice(-1, 1));
}
}, [Data.sections]);
return (
<div className={styles.sections}>
{sectionList.map(item => (
<div>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})`}
}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${lastItem ? styles.separator : ""}`}></span>
</div>
)}
</div>
);
};
As you see, I separated the generation of the data from the jsx, which makes the code more easy to understand and rebuild, I find.
const Section = () => {
return (
<>
<div className={styles.sections}>
{Data.sections.map((item, id) => (
<div key={id}>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})` }
}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${id < (Data.sections.length - 1) ? styles.separator : ""}`}></span>
</div>
))
}
</div>
</>
);
};
or
const Section = () => {
return (
<>
{Data.sections.map((item, id) => (
<div key={id}>
<div className={styles.sections} key={id}>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})` }
}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
</div>
{ id < (Data.sections.length - 1) &&
<span className={styles.separator}></span>
}
</div>
))
}
</>
);
};
I have a navbar that uses eventKeys to switch between the buttons
const CustomNav = ({ active, onSelect, ...props }) => {
return (
<Nav
{...props}
activeKey={active}
onSelect={onSelect}
style={{ marginBottom: "15px" }}>
<Nav.Item eventKey='all'>All</Nav.Item>
<Nav.Item eventKey='movies'>Movies</Nav.Item>
<Nav.Item eventKey='shows'>Shows</Nav.Item>
<Nav.Item eventKey='people'>People</Nav.Item>
</Nav>
);
};
I did this:
const Content = () => {
if (this.state.active === "all") {
return (
<div>
{trending.results &&
trending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
} else if (this.state.active === "movies") {
return (
<div>
{trendingMovies.results &&
trendingMovies.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
};
Called it here:
return (
<div className='Home'>
<FlexboxGrid justify='center'>
<Panel bordered header='Trending today!'>
<CustomNav
className='customNav'
appearance='subtle'
active={active}
onSelect={this.handleType}
/>
<Content />
<Pagination
{...this.state}
style={{ marginTop: "15px" }}
maxButtons={5}
size='sm'
pages={totalPages}
activePage={this.state.activePage}
onSelect={this.handlePage}
/>
</Panel>
</FlexboxGrid>
</div>
);
}
}
To display the correct data for each tab, but when I'm on the movies tab it shows all the data from the first "all" tab + data on the "movies" tab. I wanna show each data individually corresponding to the correct tab which is controlled by "this.state.active". Tried a switch statement too and that did not work
you are using the arrow syntax
const Content = () => { ... }
and also using this.state variable in your code !!!
if you want to use this.state, then you want to use the class syntax, like
class Content extends React.Component { ... }
but don't mix the two styles.
what you are probably wanting to do is to send the active variable as a prop
try:
const Content = ({active}) => {
if (active === 'all') {
return (...)
} else if (active === 'movies') {
return (...)
}
return null
}
and where you are calling the component you send the active value in as a prop
<Content active={active} />
Note also that you are using the variables trending and trendingMovies and it is unclear where those come from, you may need to send those via props also.
Now you can also leave the if..else logic outside of your Content component like so
const Content = ({myTrending}) => {
return (
<div>
{myTrending.results &&
myTrending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
and then where you call that component you have
<Content
myTrending={active === 'all' ? trending : trendingMovies}
/>
You need to pass active and other variables as props to the Content component, since it doesn't access them otherwise:
const Content = ({active, trending=[], trendingMovies=[]}) => {
if (active === "all") {
return (
<div>
{trending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
} else if (active === "movies") {
return (
<div>
{trendingMovies.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
};
return (
<div className='Home'>
<FlexboxGrid justify='center'>
<Panel bordered header='Trending today!'>
<CustomNav
className='customNav'
appearance='subtle'
active={active}
onSelect={this.handleType}
/>
<Content active={this.state.active} trending={this.state.trending} trendingMovies={this.state.trendingMovies} />
<Pagination
{...this.state}
style={{ marginTop: "15px" }}
maxButtons={5}
size='sm'
pages={totalPages}
activePage={this.state.activePage}
onSelect={this.handlePage}
/>
</Panel>
</FlexboxGrid>
</div>
);
}
}