Remove previous content when new one is rendered through a condition (React) - reactjs

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>
);
}
}

Related

My console are screaming about the React KEY but I didn't see the error. Can you help me?

I see this warning in console. But where is my mistake? I have Warning: Each child in a list should have a unique "key" prop. but I put the KEY PROP in all components what I render with map function.
One warning in this map function:
{data && data.map(item => (
<Card key={item.id}>
<CardTitle>{item.title}</CardTitle>
<CardPrice>{item.price}</CardPrice>
<CardDescription>{item.description}</CardDescription>
<CardItems>
{item.benefits.map(benefitsItem => (
<CardItem>
<CheckCircleIcon />
<CardItemText>{benefitsItem}</CardItemText>
</CardItem>
))}
</CardItems>
<StyledPopup
trigger={<CardButton className="BlackButton">Замовити сайт</CardButton>}
modal
nested
lockScroll
>
{close => (
<div className='modal'>
<button className="closeModal" onClick={close}>×</button>
<Feedback
isPlan={{
name: item.title,
description: item.description,
price: item.price
}}
/>
</div>
)}
</StyledPopup>
</Card>
))}
And Other warning in this component:
<Navigation>
{cards && cards.map(item => renderLinks(item))}
</Navigation>
<CardsWrapper>
{cards && cards.map(item => renderCard(item))}
</CardsWrapper>
There is the render functions.
const renderCard = (cardData) => {
if(cardData.cardId === activeCard){
return(
<Card key={cardData.cardId}>
<ImageWrapper>
<Image src={cardData.cardImage} />
</ImageWrapper>
<CardInfoWrapper>
<CardTitle>{cardData.cardTitle}</CardTitle>
<CardDescription>
{cardData.cardDescription}
</CardDescription>
<Pluses>
{cardData.cardOpportunities && cardData.cardOpportunities.map(opportunity => (
<Plus>
<Ok><CheckCircleIcon /></Ok>
{opportunity}
</Plus>
))}
</Pluses>
</CardInfoWrapper>
</Card>
)
}
}
And finnely
const renderLinks = (cardData) => {
if(cardData.cardId === activeCard) {
return(
<div key={cardData.cardId}>
<NavigationItem
className="navigationLink"
width={cardData.cardLinkWidth}
active
>
{cardData.cardLink}
</NavigationItem>
</div>
)
} else {
return(
<div key={cardData.cardId}>
<NavigationItem
className="navigationLink"
width={cardData.cardLinkWidth}
onClick={() => linkClickHandler(cardData.cardId)}
>{cardData.cardLink}</NavigationItem>
</div>
)
}
}
Looks like there's a missing key prop on line 10 at
{item.benefits.map(benefitsItem => (
<CardItem>
CardItem needs a key prop.
Each CardItem within the Card also needs it's own key as there are multiple CardItem components mapped from the benefits array. i.e <CardItem key={benefitsItem.id}/>

how to make a search bar reactjs

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>
))}
</>
);
}

Cart value resets on browser refresh

In ECommerce React project, I've created cart when clicked, changes to 'In Cart' and is then disabled which shows the product is in cart and can't be clicked back, but, when browser is refreshed Cart value resets back.
Following is the code reference
Product.js
export default class Product extends Component {
render() {
const {id, title, img, price, inCart} = this.props.product;
const dataValue = JSON.parse(localStorage.getItem('myCart'))
return (
<ProductWrapper className="col-9 mx-auto col-sm-6 col-lg-3 my-3 " >
<div className="card" >
<ProductConsumer>
{(value) => (
<div className="img-container p-3" >
<img style={imageSize} src={img} alt="product"
className="card-img-top center img-fluid img-responsive"/>
<button className="cart-btn" disabled={inCart?true:false}
onClick={() => {value.addToCart(id)}}>
{console.log('DATA VALUE', dataValue)}
{ inCart ? (
<p className="text-capitalize mb-0" disabled>
{" "}
In Cart</p>
) : (
<i className="fas fa-shopping-cart"/>
)}
</button>
</div>)}
</ProductConsumer>
</div>
</ProductWrapper>
);
}
}
ProductList.js (Mapping list of products)
export default class ProductList extends Component {
render() {
return (
<React.Fragment>
<ProductConsumer>
{value => {
return value.products.map((product, key) => {
return <Product key={product.id} product={product} />;
});
}}
</ProductConsumer>
</React.Fragment>
);
}
}
App.js
class App extends Component {
render() {
return (
<React.Fragment>
<Route render={({location}) => (
<Switch location={location}>
<Route exact path="/" component={ProductList}/>
</Switch>
)} />
</React.Fragment>
);
}
}
export default App;
I've tried with localStorage but no effect. What can be done to make the cart value store in localStorage, so that when refreshed 'In Cart' remains. Any appropriate solution?
Following is the codesandbox link: https://codesandbox.io/s/tdgwm
<button
className="cart-btn"
disabled={inCart ? true : false}
onClick={() => {
value.addToCart(id);
localStorage.setItem("added", "In Cart");
console.log(localStorage.getItem("added"));
}}
>
{localStorage === null ? (
<i className="fas fa-shopping-cart" />
) : (
<p className="text-capitalize mb-0" disabled>
{" "}
{localStorage.getItem("added")}
</p>
)}
</button>
I took a quick look at this. The above code will get the localStorage item set and render the in cart value. However, I will leave it to you to apply it to the individual product and update the conditionality for the icon/in cart piece. I'll be on for a little bit longer if you have any questions. But basically you need to see if localstorage is set prior to rendering.
It is because you don't check the local storage inCart value in the Product.js
I have added another condition like below and check it within inCart in the conditions
...
...
const localInCart = dataValue
&& dataValue.find(i => i.id === id)
&& dataValue.find(i => i.id === id).inCart;
...
...
...
(inCart || localInCart) ? ...)
you can see the result on codesandbox; https://codesandbox.io/s/mobile-store-ufxgp?fontsize=14&hidenavigation=1&theme=dark

How to add Element between react children with Some condition?

I need to add element between each children when some props values satisfied
how to do that
"only add element if the prop value is space is true"
now i did like this
const style={
height:"10px"
}
if(this.props.space){
style.width=30;
}
render(){
return(
{React.Children.map(children, (child, i) => {
return (
<React.Fragment>
<span style={style}></span>
<React.Fragment>{child}</React.Fragment>
</React.Fragment>
);
})}
)
}
So is it the span you only want if space is true?
Then you could do something like this
{React.Children.map(children, (child, i) => {
return (
<React.Fragment>
{this.props.space && <span style={style}></span>}
<React.Fragment>{child}</React.Fragment>
</React.Fragment>
);
})}
The concept is explained here https://reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator
It works because true && expression will evaluate to expression, and false && expression will evaluate to false.
Span is an inline element and doens't take width and height. You can instead return a div element conditionally and and align the child and the div element horizontally like
function App({ children, space }) {
const style = {
height: "10px",
width: "30px",
backgroundColor: "red",
display: "inlin"
};
return (
<React.Fragment>
{React.Children.map(children, (child, i) => {
return (
<React.Fragment>
<div style={{ display: "flex" }}>
{space ? <div style={style} /> : null}
<div>{child}</div>
</div>
</React.Fragment>
);
})}
</React.Fragment>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App space>
<div>Hello</div>
</App>,
rootElement
);
Working demo
However conditionally adding an element to provide space to the left doesn't seem to be a good idea. You can instead add class and add property like margin-left or add a style
return (
<React.Fragment>
{React.Children.map(children, (child, i) => {
return (
<React.Fragment>
<div style={{marginLeft: '30px'}}>{child}</div>
</React.Fragment>
);
})}
</React.Fragment>
);

ReactJS - read json value, first level values are readable but, second level showing error

This is code
const UserItem = (user, index) => (
<div key={index} className="accordion__item js-accordion-item">
<div className="accordion-header js-accordion-header">{(index+1)} . {user.invoiceId}
</div>
<div className="accordion-body js-accordion-body">
<div className="accordion-body__contents">
{user.sender.city}
<button href="#" onClick={handleClick} id={user.invoiceId}>
Click me
</button>
</div>
</div>
</div>
);
You probably used your component UserItem like this in your code
{
users.map((user, index) => <UserItem user={user} index={index} />)
}
In this case you must declare your component like this
const UserItem = ({user, index}) => ( ... );
instead of
const UserItem = (user, index) => ( ... );
Finally you should set the key property on UserItem, not on the div inside UserItem so basically you shoud write
{
users.map((user, index) => <UserItem user={user} index={index} key={index} />)
}
or event better with a unique id
{
users.map((user, index) => <UserItem user={user} index={index} key={user.invoiceId}/>)
}

Resources