How do you use multiple react components on the same element? - javascript

I would like to hover a div, get the image inside that div, show it and then make the image follow my cursor. The hover component is working but then I can't make the follow cursor component work. How would you go about doing this ?
Component number 1 :
import React from 'react';
class FollowMouse extends React.Component {
state = {
xPos: 0,
yPos: 0
};
onMouseMove(e) {
this.setState({
xPos: e.screenX,
yPos: e.screenY
});
}
render() {
return (
<div
onMouseMove={this.onMouseMove.bind(this)}
className="img-ctn"
>
{this.props.children(this.state.xPos, this.state.yPos)}
</div >
);
}
}
export default FollowMouse;
Component 2 :
import React from 'react';
class HoverProject extends React.Component {
state = {
isHovered: false,
};
onMouseEnter() {
this.setState({ isHovered: true });
}
onMouseLeave() {
this.setState({ isHovered: false });
}
render() {
return (
<div
onMouseEnter={this.onMouseEnter.bind(this)}
onMouseLeave={this.onMouseLeave.bind(this)}
className="project-item"
>
{this.props.children(this.state.isHovered)}
</div >
);
}
}
export default HoverProject;
and then the parent component.
import React from 'react';
// modules
import HoverProject from '../modules/HoverProject';
import FollowMouse from '../modules/FollowMouse';
import VLEC from '../images/vlec.png';
class ProjectList extends React.Component {
constructor(props) {
super(props);
this.sels = {
state: 'active'
};
};
render() {
return (
<div className="project-list module">
<div className="sectionTitle">Project I've worked on</div>
{this.props.data.map((res, i) => (
<HoverProject key={i}>
{
isHovered =>
<div className="inner-ctn">
{/* <FollowMouse /> */}
<img className={"project-image " + (isHovered ? this.sels.state : "")} src={VLEC} alt="VLEC" />
<div className="header">
<div className="number">0{res.id + 1}</div>
<div className="name">{res.nomProjet}</div>
</div>
<div className="item-ctn">
<div className="categ">{res.categProjet}</div>
<div className="roles">{res.roles}</div>
<div className="date">{res.date}</div>
</div>
</div>
}
</HoverProject>
))}
</div>
);
}
}
export default ProjectList;
I have no idea what to do with the other component, can you even render childs inside a parent like this ?
I would like pass the props from FollowMouse as style attributes of my img element.

I'm not 100% sure I follow your question, and I don't know what you're expecting this.props.children to do when invoked as a function, but if you want to add props to a child element you can do so via React.cloneElement:
const ParentComponent ({children}) => (
<div>
{React.cloneElement(
React.Children.only(children), // clone the only child...
{ style: { left: xPos, top: yPos} } // ...and add new props
)}
</div>
)
Given:
<ParentComponent>
<div>Wookies and Hats</div>
</ParentComponent>
The child component will get the additional props, the equivalent of:
<ParentComponent>
<div style={{left: xPos, top: yPos}}>Wookies and Hats</div>
</ParentComponent>

Related

to display a different component with each click (using hooks)

I want to display a different component with each button click.
I'm sure the syntax is wrong, can anyone help me? The browser doesn't load
I would love an explanation of where I went wrong
One component (instead of HomePage) should display on the App component after clicking the button. Help me to understand the right method.
Thanks!
App.js
import React, {useState} from 'react';
import './App.css';
import Addroom from './components/Addroom.js'
import HomePage from './components/HomePage.js'
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button onClick={()=>{setFlage({flag:true})}}>Addroom</button>
<button onClick={()=>{setFlage({flag:false})}}>HomePage</button>
{setState({flag}) && (
<div><Addroom index={i}/></div>
)}
{!setState({flag}) && (
<div><HomePage index={i}/></div>
)}
</div>
)
}
export default App;
HomePage
import React from 'react'
export default function HomePage() {
return (
<div>
HomePage
</div>
)
}
Addroom
import React from 'react'
export default function Addroom() {
return (
<div>
Addroom
</div>
)
}
I didn't test it but as i can see it should be something like this:
<button onClick={()=>setFlage(true)}>Addroom</button>
<button onClick={()=>setFlage(false)}>HomePage</button>
{flag && (
<div><Addroom index={i}/></div>
)}
{!flag && (
<div><HomePage index={i}/></div>
)}
You need to call setFlage function with argument of Boolean saying true or false and it changes the flag variable that you want to read.
Try the following.
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button
onClick={() => {
setFlage(true);
}}
>
Addroom
</button>
<button
onClick={() => {
setFlage(false );
}}
>
HomePage
</button>
{flag ? <Addroom /> : <HomePage /> }
</div>
);
}
You are missing render methods and also you should use setState for reactive rendering.( when you use state variables and once value changed render method will rebuild output so this will load your conditinal component.
https://jsfiddle.net/khajaamin/f8hL3ugx/21/
--- HTML
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Home</div>;
}
}
class Contact extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Contact</div>;
}
}
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
flag: false,
};
}
handleClick() {
this.setState((state) => ({
flag: !state.flag,
}));
console.log("hi", this.state.flag);
}
getSelectedComp() {
if (this.state.flag) {
return <Home></Home>;
}
return <Contact></Contact>;
}
render() {
console.log("refreshed");
return (
<div>
<h1>
Click On button to see Home component loading and reclick to load back
Contact component
</h1
<button onClick={() => this.handleClick()}>Switch Component</button>
{this.getSelectedComp()}
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"));

can't render react-modal as reusable component

I have an app with a list of meal, and clicking on a meal should make a modal appear on the screen with further information about the meal.
So I am trying to code a basic modal as a reusable component using the react-modal package.
However when I try to 'activate' the modal it does not work. the openModal method does get fired but the modal does not show up on the screen.
App.js:
import React from 'react';
import MealCard from './MealCard';
import MealModal from './MealModal';
export default class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false
}
this.openModal = this.openModal.bind(this);
};
openModal() {
this.setState({modalIsOpen: true});
console.log(this.state.modalIsOpen);
}
render() {
return (
<div>
<div className="app-wrapper" style={{display: 'flex'}}>
<div className="container">
<div className="row">
{[...Array(20)].map((x, i) =>
<div className="col-sm-6 col-xs-12 " key={i} onClick={this.openModal}>
<MealCard />
</div>
)}
</div>
</div>
</div>
<MealModal isOpen={this.state.modalIsOpen}/>
</div>
);
}
}
MealModal.js
import React from 'react';
import Modal from 'react-modal';
const customStyles = {
content : {
top : '50%',
left : '50%',
right : 'auto',
bottom : 'auto',
marginRight : '-50%',
transform : 'translate(-50%, -50%)'
}
};
Modal.setAppElement('#app')
export default class MealModal extends React.Component {
constructor(props) {
super(props);
this.state = {
modalId: 0,
modalIsOpen: false
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
componentWillMount() {
this.setState({modalId: 3})
}
openModal() {
this.setState({modalIsOpen: true});
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
return (
<Modal
isOpen={this.props.modalIsOpen}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Meal Modal"
>
<div className="modal-wrapper">
<div className="container text-center">
<h1>Hello</h1>
<h2>ID of this modal is {this.state.modalId}</h2>
<h3>This is an awesome modal.</h3>
<button onClick={this.closeModal}>close</button>
</div>
</div>
</Modal>
)
}
}
You're passing isOpen as props and using modalIsOpen (props) in MealModal component.
As mentioned in the comment, you can just use isOpen={this.props.isOpen}. There's no sense to use two states for serving the same purpose, one is modalIsOpen in App component and other is modalIsOpen in MealModel component

Change react-modal data dynamically

I have a Parent component, App.js and a Child component, MealModal.js. When a user click on a specific meal card, it raises a modal that should display further information about the meal.
Hence I try to find a way to dynamically change the modals's data, depending on which meal card is clicked
I have tried to pass the id of the meal to the onClick={this.openModal} function and set the state of modalId is the function. But I got the following error:
Warning: Cannot update during an existing state transition (such as
within render or another component's constructor). Render methods
should be a pure function of props and state; constructor side-effects
are an anti-pattern, but can be moved to 'componentWillMount'.
Here are my components so far:
App.js:
import React from 'react';
import MealCard from './MealCard';
import MealsMap from './MealsMap';
import MealsFilters from './MealsFilters';
import MealModal from './MealModal';
export default class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false,
modalId: 0
}
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
};
openModal() {
this.setState({modalIsOpen: true});
};
closeModal() {
this.setState({modalIsOpen: false});
};
render() {
return (
<div>
<MealsFilters/>
<div className="app-wrapper" style={{display: 'flex'}}>
<div className="container">
<div className="row">
{[...Array(20)].map((x, i) =>
<div className="col-sm-6 col-xs-12 " key={i} onClick={this.openModal}>
<MealCard />
</div>
)}
</div>
</div>
<MealsMap/>
</div>
<MealModal modalIsOpen={this.state.modalIsOpen} closeModal={this.closeModal} modalId={this.state.modalId}/>
</div>
);
}
}
MealModal.js
import React from 'react';
import Modal from 'react-modal';
const customStyles = {
content : {
}
};
Modal.setAppElement('#app')
export default class MealModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
isOpen={this.props.modalIsOpen}
onRequestClose={this.props.closeModal}
style={customStyles}
contentLabel="Meal Modal"
>
<div className="modal-wrapper">
<div className="container text-center">
<h1>Hello</h1>
<h2>ID of this modal is {this.props.modalId}</h2>
<h3>This is an awesome modal.</h3>
<button onClick={this.props.closeModal}>close</button>
</div>
</div>
</Modal>
)
}
}
Any idea on how I could do this ?
Ok so I found the solution:
First, I changed onClick={this.openModal} in the parent comoponent to onClick= () => {this.openModal}
Second, I add the id as a parameter:
onClick= () => {this.openModal(i)}
Finally: update the openModal function:
openModal(modalId) {
this.setState({modalIsOpen: true,
modalId});
};
And it works.
openModal(modalId) {
this.setState({
modalId,
modalIsOpen: true
});
};
and modify the call function as
<div className="col-sm-6 col-xs-12" key={i} onClick={() => this.openModal(x) } >
<MealCard/>
</div>

React js: Accessing state of other components

I have a component built using the below code. The aim is to add a class on the card to highlight it when the button inside it is clicked. However, the below code works on the first click but doesn't work for the subsequent clicks.
I understood that I have to set the clicked state of other elements to false when I remove the class. How can this be done?
import React, { Component } from 'react';
import './PricingCard.css';
class PricingCard extends Component {
constructor(){
super();
this.state = {
clicked : false
}
}
makeSelection(){
let elems = document.getElementsByClassName('Card');
for(var i=0;i<elems.length;i++){
elems[i].classList.remove("active");
}
this.setState({clicked: true});
}
render() {
var activeClass = this.state.clicked ? 'active' : '';
return (
<div className= {"categoryItem Card " + this.props.planName + " " +activeClass}>
<div className="cardDetails">
<div> {this.props.planName} </div>
<div className="pricing"> {this.props.price} </div>
<button onClick={this.makeSelection.bind(this)} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {this.props.footerText} </div>
</div>
</div>
);
}
}
export default PricingCard;
Wouldn't it be easier to have the logic in a parent component? Since it is "aware" of all the child Card components.
Have something like...
this.state = { selectedComponent: null };
onClick(card_id) {
this.setState({ selectedComponent: card_id });
}
...in render:
const cards = smth.map((card) =>
<Card onClick={this.onClick.bind(this, card.id)}
isActive={map.id === this.state.selectedComponent} />
Would this work?
Best way will be to lift lift the state up. Like this:
class PricingCardContainer extends React.Component {
constructor(props){
super(props);
this.state = {
selectedCard: NaN,
}
}
handleCardClick(selectedCard){ this.setState({ selectedCard }); }
render() {
return (
<div>{
this.props.dataArray.map((data, i) =>
<PricingCard
key={i}
className={this.state.selectedCard === i ? 'active': ''}
price={data.price}
onClick={() => this.handleCardClick(i)}
footerText={data.footerText}
planName={data.planName}
plan={data.plan}
/>
)
}</div>
)
}
}
const PricingCard = ({ className = '', planName, price, onClick, footerText }) => (
<div className= {`categoryItem Card ${planName} ${className}`}>
<div className="cardDetails">
<div> {planName} </div>
<div className="pricing"> {price} </div>
<button onClick={onClick} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {footerText} </div>
</div>
</div>
);
export default PricingCard;
Although it would be better to use some data id than index value.

React - How change state of single component when there are multiple instance of same component?

I have 4 boxes with component name SingleTopicBox. I would like to change color of box when user clicks on particular box. SingleTopicBox component has props topicID as a unique identifier for each box. Currently when user clicks on any of the box it's changing state of all the boxes bgDisplayColor: 'red' however, I would like to only change the color of boxes that is clicked.
index.jsx
import {React, ReactDOM} from '../../../../build/react';
import SelectedTopicPage from './selected-topic-page.jsx';
import TopicsList from './topic-list.jsx';
import topicPageData from '../../../content/json/topic-page-data.js';
export default class MultiTopicsModuleIndex extends React.Component {
constructor(props) {
super(props);
this.setTopicClicked = this.setTopicClicked.bind(this);
this.onClick = this.onClick.bind(this);
() => topicPageData;
this.state = {
isTopicClicked: false,
bgDisplayColor: 'blue'
};
};
onClick(topicID) {
this.setState({isTopicClicked: true, topicsID: topicID, bgDisplayColor: 'red'});
};
setTopicClicked(boolean) {
this.setState({isTopicClicked: boolean});
};
render() {
return (
<div className="row">
{this.state.isTopicClicked
? <SelectedTopicPage topicsVisited={this.topicsVisited} setTopicClicked={this.setTopicClicked} topicsID={this.state.topicsID} key={this.state.topicsID} topicPageData={topicPageData}/>
: <TopicsList bgDisplayColor={this.state.bgDisplayColor} topicsVisited={this.topicsVisited} onClick={this.onClick}/>}
</div>
);
}
};
topic-list.jsx
import {React, ReactDOM} from '../../../../build/react';
import SingleTopicBox from './single-topic-box.jsx';
import SelectedTopicPage from './selected-topic-page.jsx';
export default class TopicsList extends React.Component {
render() {
return (
<div className="row topic-list">
<SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="1" onClick={this.props.onClick} label="Topic"/>
<SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="2" onClick={this.props.onClick} label="Topic"/>
<SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="3" onClick={this.props.onClick} label="Topic"/>
<SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="4" onClick={this.props.onClick} label="Topic"/>
</div>
);
}
};
Single-topic-box.jsx
import {React, ReactDOM} from '../../../../build/react';
export default class SingleTopicBox extends React.Component {
render() {
var divStyle = {
backgroundColor: this.props.bgDisplayColor
};
return (
<div>
<div className="col-sm-2">
<div style={divStyle} className="single-topic" data-topic-id={this.props.topicID} onClick={() => this.props.onClick(this.props.topicID)}>
{this.props.label}
{this.props.topicID}
</div>
</div>
</div>
);
}
};
You can't seem to decide whether you want the state for the top-level component or the SingleTopicBox, but you put it in the top-level component anyway.
You should make a choice: Should the SingleTopicBox component know it's been clicked or should MultiTopicsModuleIndex remember which SingleTopicBox has been clicked?
If you are ok with multiple SingleTopicBox component having the clicked state at the same time, you should move the state and the onClick hander to SingleTopicBox. You don't need to remember both if the component has been clicked and its background color. For example, you can:
getInitialState: function() {
return {
bgDisplayColor: 'blue'
};
},
onClick() {
this.setState({bgDisplayColor: 'red'});
};
then use this.state.bgDisplayColor in the render method
If you want only one component to be clicked at any single time, so that previously clicked component goes back to unclicked when another one is clicked, you may want to have the state and the handler live in the top-level component, as you already have it in your code. The only thing you need to remember in the state is topicID, then you pass it to the TopicsList component as property like this:
<TopicsList clickedTopicID={this.state.topicID} onClick={this.onClick} />
and in TopicsList you render something like this:
render: function() {
var topics = [
{id: 1, label: 'Topic'},
{id: 2, label: 'Topic'},
{id: 3, label: 'Topic'},
{id: 4, label: 'Topic'},
];
var boxes = [];
for (var i = 0, len = topics.length; i < len; i++)
boxes.push(<SingleTopicBox
bgDisplayColor={(this.props.clickedTopicID == topics[i].id) ? 'red' : 'blue'}
topicID={topics[i].id}
onClick={this.props.onClick}
label={topics[i].label}
/>);
return (
<div className="row topic-list">
{boxes}
</div>
);
}

Categories