Hide sub-component in components generated with map() in React - reactjs

I'm working on a todo app.
When a task is clicked
setState({displayTaskMenu: true})
is called,
but I also need to hide the taskMenu on all the other Task components where displayTaskMenu is true.
What is the correct way of doing this with React?
This is the render method of Task component:
render() {
let { task } = this.props;
return (
<div onClick={this.toggleMenu}>
<div>
{task.label}
{this.state.displayTaskMenu && (<TaskMenu />)}
</div>
</div>
);
}
Tasks are sorted by days, and the render of Day component is:
render() {
return (
<div style={this.state.dayStyle}>
<span style={this.state.dateStyle}>{this.props.date}</span>
<h1>{this.props.day}</h1>
{this.props.tasks &&
this.props.tasks.map(
(task, i) => <Task key={i} task={task}/>
)}
</div>
);
}

Since only one Task component should open their TaskMenu, the Day component needs to keep track of which Task it is by using state. So, the Day component should pass in a custom onClick function to each Task an update its state.
Day component:
render() {
return (
<div style={this.state.dayStyle}>
<span style={this.state.dateStyle}>{this.props.date}</span>
<h1>{this.props.day}</h1>
{this.props.tasks &&
this.props.tasks.map(
(task, i) => <Task
key={i}
task={task}
displayMenu={this.state.displaying == i}
onClick={ () => this.setState({displaying: i}) }/>
)}
</div>
);
}
Task component:
render() {
let { task } = this.props;
return (
<div onClick={this.props.onClick}>
<div>
{task.label}
{this.props.displayMenu && (<TaskMenu />)}
</div>
</div>
);
}
Using this approach, you don't need the toggleMenu function in the Task component anymore.

Related

Component is not being returned in nested loop

I am trying to render the child component inside a nested loop. However it is not being render in the second loop(red tick). Although it is rendered normally in the first loop (blue tick). Kindly highlight why is it no rendered in the second loop.
https://i.stack.imgur.com/LFiKU.png
Codesandbox Link : https://codesandbox.io/s/competent-nova-u9rzuh?file=/src/parent.js
import React from "react";
import ProductFeaturesCards from "./ProductFeaturesCards.js";
import { Products } from "../ProductsData.js";
const ProductFeatures = ({ className = "", id }) => {
return (
<section id='product-features' className={`${className}`}>
<div className='container'>
<div className='row d-flex justify-content-center'>
<div className='col-lg-12 col-md-12 col-sm-12 col-12 py70'>
<p className='features-title'>Product Features</p>
</div>
</div>
<div className='row'>
{Products.forEach((item, i) => {
if (item.id === id) {
// return <ProductFeaturesCards data={item} key={i} />;
Object.values(item.Product_features[0]).map((feature, index) => {
console.log("ProductFeaturesCards:", feature);
return <ProductFeaturesCards data={feature} key={index} />;
});
}
})}
</div>
</div>
</section>
);
};
export default ProductFeatures;
Can you try this. Not sure if this will work
if (item.id === id) {
// return <ProductFeaturesCards data={item} key={i} />;
return Object.values(item.Product_features[0]).map((feature, index) => {
console.log("ProductFeaturesCards:", feature);
return <ProductFeaturesCards data={feature} key={index} />;
});
}
The first mistake you did was using forEach. forEach will mutate the data and will not return anything. So, Instead you need to use map which doesn't mutate and returns the result.
The 2nd mistake is the return statement not added inside the if condition for the map. So, it never gets returned and hence your first map will not receive the value.
After this you should be able to run it.
<div className="row">
{Products.map((item) => { // replaced forEach with map
if (item.id === id) {
return Object.values(item.Product_features[0]).map( // return added
(feature, index) => {
return <Card data={feature} key={index} />;
}
);
}
})}
</div>

How to change a style of an HTML element in React?

I have two React components
class App extends React.Component {
render() {
return (
<div id="appWrapper">
<ConfigureWindow />
<button id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
}
const ConfigureWindow = () => (
<div id="configureWindowWrapper">
<div id="configureWindow">
<section id="addCitySection">TODO: adding a city</section>
<div id="verticalLine"></div>
<section id="listOfCities">
<header>
<h1>Available cities</h1>
<div id="closeConfigureWindowWrapper">
<img src="..\src\images\exit.png" id="closeConfigureWindow" alt="" />
</div>
</header>
<section id="availableCities"></section>
</section>
</div>
</div>
);
I want "ConfigureWindow" to be shown when "configureClocksButton". I tried to execute it with props, state and a function but got errors. It also would be nice if you explain me how to create new React components with React functions?
You probably want to use the React.JS event onClick (https://reactjs.org/docs/handling-events.html), and a state to store the action. To create a function component, you just have to return the JSX you want to render, and use hooks (https://reactjs.org/docs/hooks-intro.html) and then do a conditional rendering (https://reactjs.org/docs/conditional-rendering.html):
const App = () => {
const [toggleConfiguration, setToggleConfiguration] = useState(false)
return (
<div id="appWrapper">
{toggleConfiguration && <ConfigureWindow />}
<button onClick{() => setToggleConfiguration(true)} id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
It's a bit difficult to understand your post, but I gather you want to click the button with id="configureClocksButton" and conditionally render the ConfigureWindow component.
You can accomplish this with some boolean state, a click handler to toggle the state, and some conditional rendering.
class App extends React.Component {
this.state = {
showConfigureWindow: false,
}
toggleShowConfigureWindow = () => this.setState(prevState => ({
showConfigureWindow: !prevState.showConfigureWindow,
}))
render() {
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={this.toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
}
A function component equivalent:
const App = () => {
const [showConfigureWindow, setShowConfigureWindow] = React.useState(false);
const toggleShowConfigureWindow = () => setShowConfigureWindow(show => !show);
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}

How on click rerender conmponent in react

When I click on a card, the loadAboutInfo function works through which I transfer data to another component and display it there. But if I click again on the same card, then it is duplicated. How can I fix it?I have check which take card id and then if it the same it render but I click again it render one more card, but i need if it already exist than new card mustn't render
loadAboutInfo=(pokemonValue,pockemonImg,pokemonId)=>{
this.setState(prevState => ({
pokemonValue:[...prevState.pokemonValue, pokemonValue],
pockemonImg,
pokemonId
}))
}
render() {
return (
<div className="wrapper">
<div className="pokemonlist__inner__cards">
<div className="pokemonlist__cards">
{this.state.pokemonList.map((value,index)=>{
let pokemonImgTemplate = this.state.pokemonImgTemplate;
let pokemonId = value.id;
let pockemonImg = pokemonImgTemplate.replace('{id}',pokemonId);
return(
<div className="pokemonListCard" key={index} onClick={()=>this.loadAboutInfo(value,pockemonImg,pokemonId)}>
<PokemonCard
pockemonImg={pockemonImg}
pokemonName={value.name}
pokemonTypes={value.types}
/>
</div>
)
})}
</div>
<PokemonLoadMore
loadMore={this.loadMore}
currentPage={this.state.currentPage}
/>
</div>
</div>
);
}
}
component where i map get data
render() {
return (
<div className="pokemon__about">
{this.props.pokemonValue.map((value,index)=>{
let totalMoves = value.moves.length;
return(
<div className="pokemon__about__wrapper" key={index}>
{this.props.pokemonId == value.id ?
<div className="pokemon__about__inner" key={index}>
<AboutImage
pockemonImg={this.props.pockemonImg}
/>
<AboutName
pockemonName={value.name}
/>
<div className="pokemon__about__table">
<AboutPokemonTypes
pokemonTypes={value.types}
/>
<table>
<AboutPokemonWeight
pockemonWeight={value.weight}
/>
<AboutPokemonMoves
totalMoves={totalMoves}
/>
</table>
</div>
</div>
:
null
}
</div>
)
})}
</div>
);
On the loadAboutInfo you can check if there is already a pokemon with the same id on pokemonValue array, something like this:
loadAboutInfo = (pokemonValue,pockemonImg,pokemonId) => {
// this will get the first element that matches the id
const exists = this.state.pokemonValue.find(pokemon => pokemon.id === pokemonId)
if (!exists) {
this.setState(prevState => ({
pokemonValue:[...prevState.pokemonValue, pokemonValue],
pockemonImg,
pokemonId
}))
}
}
So it will update the state only if the clicked pokemon isn't in the pokemonValue array

How to pass Mobx store as props to react compoent

I have this app that uses mobx, in it there is a component called "Listings" that uses some state from mobx to render a list of items.
The way it is right now, is that the Listings component gets the data it needs(store.restaurantResults[store.selectedFood]) from inside of it by using the mobx store like so:
const Listings = () => {
const store = React.useContext(StoreContext);
return useObserver(() => (
<div className="pa2">
{store.restaurantResults[store.selectedFood] &&
store.restaurantResults[store.selectedFood].map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<p>{rest.name}</p>
</div>
);
})}
</div>
));
};
But i think this is wrong, as it couples the component with the data, I want instead to pass that data via props so it can be reusable.
What is the correct way to do this? Right now my App looks like this, where it's being wrapped around a storeProvider:
function App() {
return (
<StoreProvider>
<div className="mw8 center">
<Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way"/>
<FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
<p className="b tc pt3">or...</p>
<Search />
<Listings />
</div>
</StoreProvider>
);
}
My idea is to extract everrything inside the StoreProvider into another component that has a store and returns the jsx via useObserver so that I can acces the store and then pass what i need as props to the other components. like this:
const Wapper = () => {
const store = React.useContext(StoreContext);
return useObserver(() => (
<div className="mw8 center">
<Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way" />
<FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
<p className="b tc pt3">or...</p>
<Search />
<Listings listings={store.restaurantResults[store.selectedFood]} />
</div>
))
}
And then on the listings component change the hard coded store.restaurantResults[store.selectedFood] inside to use the props that is being passes now, that is called listigs like so:
const Listings = ({listings}) => {
const store = React.useContext(StoreContext);
return useObserver(() => (
store.loading
? <Loading />
: <div className="pa2">
<div className="flex flex-wrap">
{listings &&
listings.map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<img className='object-fit' src={rest.image_url} alt="restuarant" />
<p>{rest.name}</p>
<p>{rest.location.address1}</p>
</div>
);
})}
</div>
</div>
));
};
And this works, but is this the right way to go about this?
As <Listings/> can be provided with listing and loading you can:
const Listings = ({listings, loading}) => {
if(loading) return <Loading />
return (
<div className="pa2">
<div className="flex flex-wrap">
{listings && listings.map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<img className='object-fit' src={rest.image_url} alt="restuarant" />
<p>{rest.name}</p>
<p>{rest.location.address1}</p>
</div>
);
})}
</div>
</div>
);
}
No observables used, no useObservable required.
You want to useObservables on store for listings then no reason to wrap all components with useObservable. You should wrap <Listings/> only.
I usually define my store as a global, so every component has visibility of it:
class Store {
#observable myVar
}
global.store = new Store()
And in my components i just use it:
#observer
export default class MyComponent extends React.Component {
constructor () {
super()
store.myVar = 0
}
setMyVar (a) {
store.myVar += 1
}
render () {
return <button onClick={this.setMyVar}>
Clicked {store.myVar} times
</button>
}
}

Logic for Three-dot more options menu for web applications in reactjs

I am new to ReactJs and I'm developing a social media web application. Here I have template where I have to implement a Three-dot more options menu. I tried using Bootstrap menu and react Bootstrap component menu. Both didn't work for me. What is the best way to implement this feature without using a library?
I did till toggling the menu. But on click, all the menus toggle altogether. I am not able do the toggling individually.
Here's the piece of code I did:
post.jsx
class UserPost extends Component {
state = {
overFlowMenuActive: false
};
toggleOverflowMenu = () => {
this.setState((prevState) => ({ overFlowMenuActive:
!prevState.overFlowMenuActive }));
};
closeOverflowMenu = () => {
this.setState({ overFlowMenuActive: false });
};
render() {
return (
<React.Fragment>
{this.props.posts.map((post, index) =>(
<div>
<div tabIndex='0' onBlur={this.closeOverflowMenu}>
<img src={require('../../assets/images/more.svg')} alt='' onClick={this.toggleOverflowMenu}/>
</div>
<MoreBtn options={this.state.options} overFlowMenuActive={this.state.overFlowMenuActive} />
</div>
))}
</React.Fragment>
);
}
MoreBtn.jsx
<div className={`${classes['popup-more']} ${this.props.overFlowMenuActive
? classes.expand
: classes.collapse}`}>
{this.props.options.map((option, index) => (
<div key={index}>
<img src={option.url} alt='' />
<p>{option.name}</p>
</div>
))}
</div>
You are maintaining only a single state for all UserPosts
To have each of these toggle seperately, these states should be moved into the component.
class SinglePost extends Component {
state = {
overFlowMenuActive: false
};
toggleOverflowMenu = () => {
this.setState((prevState) => ({ overFlowMenuActive:
!prevState.overFlowMenuActive }));
};
closeOverflowMenu = () => {
this.setState({ overFlowMenuActive: false });
};
render() {
return (
<div>
<div tabIndex='0' onBlur={this.closeOverflowMenu}>
<img src={require('../../assets/images/more.svg')} alt='' onClick={this.toggleOverflowMenu}/>
</div>
<MoreBtn options={this.state.options} overFlowMenuActive={this.state.overFlowMenuActive} />
</div>
);
}
class UserPost extends Component {
render() {
return (
<React.Fragment>
{this.props.posts.map((post, index) =>(
<SinglePost post={post} />
))}
</React.Fragment>
);
}
This way, the button for only one component is toggled at a time

Resources