How to pass an onMouseEnter function into child component - reactjs

I would like to update my parent component's state when hovering over a child component. Basically, this parent component consists of an h1 and four components called Box. Each component has a title prop, which the component renders internally. What I would like to have happen is when a user hovers over a Box, the hovered state of the parent component changes to the title of the hovered child. Here is essentially what I have right now:
class Home extends React.Component {
constructor(props) {
super(props)
this.state = {
hovered: 'none'
};
this.handleHover = this.handleHover.bind(this);
}
handleHover = (n) => {
this.setState((n) => ({
hovered: n
}));
};
handleHoverOut = () => {
this.setState(() => ({
hovered: 'none'
}));
};
render() {
return (
<h1 className={this.state.hovered} >TITLE TITLE TITLE</h1>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title1'/>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title2'/>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title3'/>
<Box oME={ this.handleHover } oML={ this.handleHoverOut } title='Title4'/>
)
}
class Box extends React.Component {
render() {
return(
<section onMouseEnter={() => { this.props.oME(this.props.title)}} onMouseLeave={() => { this.props.oML()}}>
...
</section>
}
I know this might not be 100% the way to go about it, but I think I'm somewhat on the right track! Please help me try to improve my code here, since I'm still learning the basics of React!

I created codesendbox where you can check the solution.
There were couple of issues in your code that I fixed there. Your state was not being displayed properly as title and there were unneeded callback functions.

Related

ReactJS Assigning Value inside a class component method to props

I'm trying to access the index of the selected tab in a React component so as to map it to props as follows:
class AttendanceDetail extends React.Component {
handleSelect(key, props) {
console.log(key)
props.index = key;
}
render(){
const {single_class, courses, attendances} = this.props;
// console.log(this.state);
if(single_class) {
return(
<div className='container content-section'>
// Some irrelevant Code
<Tabs defaultActiveKey={0} onSelect={this.handleSelect} id="uncontrolled-tab-example">
{ courses.map((course, index) => {
return (
<Tab eventKey={index} title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>
)
})}
</Tabs>
</div>
)
} else {
return (
<div className='container content-section'>
Loading Unit details...
</div>
);
}
}
}
So basically the handleSelect method is what determines the index of the selected tab and logs it to the console. The problem is, I'm tring to map that key (index) to props so as to access it else where but to no avail. Could someone help me out? What am I missing?
You're not supposed to set the component's props, only read. You can use state within the component:
export class Wrapper extends React.Component {
constructor() {
this.state = {
index: 0 //initial state
}
}
handleSelect(index, props) {
this.setState({index})
}
render() {
return (
<span>{this.state.index}</span>
)
}
}
you can read more on the official docs.
if i understood the scenario correctly, you need to log index value of the currently active tab. try using onFocus event handler to get the index value of the currently visible tab and set the state that will be used by handleSelect
constructor(props){
super(props);
this.state = {
index:''
}
}
the handler definition
setIndex = (index) => {
this.setState({index})
}
update handleSelect
handleSelect(index) {
console.log(index)
// call event handler of parent component eg: this.props.getIndex(index);
}
update tabs component handler
<Tabs defaultActiveKey={0} onSelect={() => {this.handleSelect(this.state.index)}} id="uncontrolled-tab-example">
call handler on focus of tab
<Tab
onFocus={() => {this.setIndex(index)}}
eventKey={index}
title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>

How do I limit the user to only selecting one component?

I have the following code that simply constructs blocks for our products and the selected state allows the component to be selected and unselected. How can I figure out which of these components are selected and limit the user to only selecting one at a time. This is ReactJS code
import React from 'react';
export default class singleTile extends React.Component{
constructor(props){
super(props);
this.title = this.props.title;
this.desc = this.props.desc;
this.svg = this.props.svg;
this.id = this.props.id;
this.state = {
selected: false
}
}
selectIndustry = (event) => {
console.log(event.currentTarget.id);
if(this.state.selected === false){
this.setState({
selected:true
})
}
else{
this.setState({
selected:false
})
}
}
render(){
return(
<div id={this.id} onClick={this.selectIndustry}className={this.state.selected ? 'activated': ''}>
<div className="icon-container" >
<div>
{/*?xml version="1.0" encoding="UTF-8"?*/}
{ this.props.svg }
</div>
</div>
<div className="text-container">
<h2>{this.title}</h2>
<span>{this.desc}</span>
</div>
</div>
)
}
}
You need to manage the state of the SingleTile components in the parent component. What i would do is pass two props to the SingleTile components. A onClick prop which accepts a function and a isSelected prop that accepts a boolean. Your parent component would look something like this.
IndustrySelector.js
import React from 'react';
const tileData = [{ id: 1, title: 'foo' }, { id: 2, title: 'bar' }];
class IndustrySelector extends Component {
constructor(props) {
super(props);
this.state = { selectedIndustry: null };
}
selectIndustry(id) {
this.setState({ selectedIndustry: id });
}
isIndustrySelected(id) {
return id === this.state.selectedIndustry;
}
render() {
return (
<div>
{tileData.map((data, key) => (
<SingleTile
key={key}
{...data}
onClick={() => this.selectIndustry(data.id)}
isSelected={this.isIndustrySelected(data.id)}
/>
))}
</div>
);
}
}
The way this works is as follows.
1. Triggering the onClick handler
When a user clicks on an element in SingleTile which triggers the function from the onClick prop, this.selectIndustry in the parent component will be called with the id from the SingleTile component.
Please note that in this example, the id is remembered through a
closure. You could also pass the id as an argument to the function of
the onClick prop.
2. Setting the state in the parent component
When this.selectIndustry is called it changes the selectedIndustry key of the parent component state.
3. Updating the isSelected values form the SIngleTile components
React will automatically re-render the SingleTile components when the state of the parent component changes. By calling this.isIndustrySelected with the id of the SingleTile component, we compare the id with the id that we have stored in the state. This will thus only be equal for the SingleTile that has been clicked for the last time.
Can you post your parent component code?
It's not so important, but you can save some time by using this ES6 feature:
constructor(props){
super(props);
const {title, desc, svg, id, state} = this.props;
this.state = {
selected: false
}
}

Do react render props cause remounting of the child components?

I was just wondering if people know if using the "render props" pattern causes excessive mounting/unmounting of the child component.
For example, adapting from the react docs (https://reactjs.org/docs/render-props.html):
<Mouse>
{mouse => (
<ShowMousePosition mouse={mouse}/>
)}
</Mouse>
class ShowMousePosition extends React.Component {
componentDidMount(){
console.log('mounting!')
}
render () {
const {mouse} = this.props
return (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)
}
}
I know the react docs say:
Using a render prop can negate the advantage that comes from using React.PureComponent if you create the function inside a render method. This is because the shallow prop comparison will always return false for new props, and each render in this case will generate a new value for the render prop.
But, will "mounting!" be called over and over as the user moves the mouse around?
Thanks!
I went ahead and tried to answer my own question using a fiddle. It appears that "mounting!" is not called over and over again:
https://jsfiddle.net/69z2wepo/186690/
Here is the code:
class Hello extends React.Component {
render() {
return <Mouse>
{mouse => (
<ShowMousePosition mouse={mouse}/>
)}
</Mouse>
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: 800, width: 800 }} onMouseMove={this.handleMouseMove}>
{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
*/}
{this.props.children(this.state)}
</div>
);
}
}
class ShowMousePosition extends React.Component {
componentDidMount(){
console.log('mountin!')
}
render () {
const {mouse} = this.props
return (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
componentDidMount is only called once but componentDidUpdate will be called multiple times along with your render function every time your state/props is changed.

Open Drawer component from Toolbar component in React

I have written a Nav component which renders react-md Drawer component. Two functions in this file control Drawer's visibility-
showDrawer = () => {
this.setState({ visible: true });
};
hideDrawer = () => {
this.setState({ visible: false });
};
This component is rendered at App level.
I have a page with Toolbar component. This toolbar has a menu button that should open the Drawer in Nav component.
class Catalog extends React.Component {
render() {
return (
<div>
<Toolbar themed fixed title="Catalog" nav={<Button icon onClick={this.showDrawer}>menu</Button>} />
....
How can I implement it in React?
You're close. Assuming we're just using plain-old React, you'll want to call the constructor function (which requires calling super as well), bind the 'this' context to the functions, define those functions and call them in the toolbar code you have below.
Assuming we're not involving Redux and using component state, you'll also want to set an initial state for your app (going with false for my example).
When I do this myself, I'll usually condense the functions into one 'toggle' function which you can do nicely with a ternary, checking the state to decide whether to open or close the nav.
Not sure if that fully answers your question, but hopefully it's a start.
All together, that looks something like this. (Note that the current implementation will only open the drawer and not close it):
export default class Catalog extends React.Component {
constructor(props) {
super(props);
this.hideDrawer = this.hideDrawer.bind(this);
this.showDrawer = this.showDrawer.bind(this);
this.getNavStyle = this.getNavStyle.bind(this);
this.state = { visible: false }
}
getNavStyle() {
return this.state.visible
? {
overflow: 'scroll',
width: '200px',
}
: {
overflow: 'visible',
letterSpacing: '2px',
width: '200px',
};
}
showDrawer = () => {
this.setState({ visible: true });
};
hideDrawer = () => {
this.setState({ visible: false });
};
render() {
return (
<div>
<Toolbar themed fixed title="Catalog"
nav={
<Button icon onClick={this.showDrawer}>
menu
</Button>}
/>
</div>
)
}
}

How to share a property with React components?

I'm new to React and I have a question about sharing properties from one component to another. For example, I want a parent component that has a "visible" function that I can pass to other child components.
Example:
CustomInput visible="true";
CustomDropDown visible="false"
I'd like to know the best way to do this, respecting good practices. Thank you for your help!
Real simple. You can pass methods as props. Suppose you have a parent, or Higher Order Component (HOC), you could do something like this:
class Parent extends React.Component {
logWord = (word) => {
console.log(word);
}
render () {
return <ChildComponent handleLogging={ this.logWord } />
}
}
Then, in the ChildComponent, you simply access the method from props. For instance:
class ChildComponent extends React.Component {
render () {
return (
<div onClick={ this.props.handleLog.bind(null, 'Logged!') }>Click me to log a word!</div>
}
}
}
So, in your example, if you wanted a method that existed on the parent that updated a visibility attribute on your state, you could write:
class Parent extends React.Component {
constructor () {
this.state = {
visible: false
}
}
setVisible = (bool) => {
this.setState({ visible: bool });
}
render () {
return <ChildComponent updateVisible={ this.setVisible } visible={ this.state.visible } />;
}
}
ChildComponent:
class ChildComponent extends React.Component {
render () {
return (
<div>
<div onClick={ this.props.updateVisible.bind(null, true) }>Set me to visible!</div>
<div onClick={ this.props.updateVisible.bind(null, false) }>Set me to invisible!</div>
{ this.props.visible && <div>I'm visible right now!</div> }
</div>
}
}
}

Resources