How does the ReactBootstrap OverlayTrigger container property work? - reactjs

I have a popover inside OverlayTrigger.
I define it as
const myOverlayTrigger = <ReactBootstrap.OverlayTrigger
placement='bottom' overlay={<ReactBootstrap.Tooltip>...</ReactBootstrap.Tooltip>}>
{myContent}
</ReactBootstrap.OverlayTrigger>;
Then I render it inside one of my elements like that:
<li>{myOverlayTrigger}</li>
I want to render OverlayTrigger itself inside <li> but it renders inside body, as defined in documentation. I'm trying to use container attribute to render it inside parent <li>.
First, I tried to assign ID to <li> and pass this ID as a string to container=... (which isn't a best way).
Second, I tried to create additional element <span></span> and render it inside along with {myOverlayTrigger}. Also I pass it (assigned to variable) to container attribute
const c = <span></span>;
... container={c} ...
<li>{c} {myOverlayTrigger}</li>
Both approaches consistently gives an error not a dom element or react component.
Obviously assigning <li>...</li> itself as a container doesn't work either as it being defined after myOverlayTrigger is defined.
Question: how to use it right?

ReactBootstrap.Overlay is recommended for the reason listed in the document.
The OverlayTrigger component is great for most use cases, but as a
higher level abstraction it can lack the flexibility needed to build
more nuanced or custom behaviors into your Overlay components. For
these cases it can be helpful to forgo the trigger and use the Overlay
component directly.
For your case, the code below renders the ReactBootstrap.Overlay component into a list item with React ref attribute.
getInitialState() {
return (
show: false
);
},
render() {
return (
<ul>
<li ref="dest" onClick={ () => {
this.setState( { show: !this.state.show } );
}}>my contents</li>
<ReactBootstrap.Overlay placement="bottom"
show={ this.state.show } container={ this.refs.dest }>
<ReactBootstrap.Tooltip>tooltip</ReactBootstrap.Tooltip>
</ReactBootstrap.Overlay>
</ul>
);
}
When the tooltip is displayed by clicking, the resulting HTML would be
<ul data-reactid=".0.1.0.0.0.0.0.1.1.1.1:$3.1.1">
<li data-reactid=".0.1.0.0.0.0.0.1.1.1.1:$3.1.1.0">
contents
<div>
<div role="tooltip" class="fade in tooltip right" data-reactid=".3">
<div class="tooltip-arrow" data-reactid=".3.0"></div>
<div class="tooltip-inner" data-reactid=".3.1">My tooltip</div>
</div>
</div>
</li>
<span data-reactid=".0.1.0.0.0.0.0.1.1.1.1:$3.1.1.1">,</span>
<noscript data-reactid=".0.1.0.0.0.0.0.1.1.1.1:$3.1.1.2"></noscript>
</ul>

Related

Show / Hide element by data attribute (react=

How can i show/hide elements in a list using react? I have buttons with data-attribute
and i want when click show elements with this classname and hide the others.
example here:
class ModelosItems extends React.Component {
handleCheck(e) {
console.log(e.currentTarget.dataset.gama );
}
render() {
return (
<section className="section">
<div className="container">
<h2 className="title is-size-4 has-text-centered is-uppercase has-text-weight-bold">Gama kia</h2>
<div className="tabs-container">
<div className="fade"></div>
<div className="tabs">
<ul>
<li className="is-active">Gama Completa</li>
<li>Cidatidos e Familiares</li>
<li>Suv e Crossover</li>
</ul>
</div>
</div>
<ModelsList />
</div>
</section>
);
}
};
export default ModelosItems;
Thank you!
For hide the other datas and just show the special data, you have to filter your list. After, you can for example, add a CSS class whose this class hide the other datas, and you'll have just your special data showed.
A tutorial : https://www.youtube.com/watch?v=HqQ-kOchqHM
Utilize local state for this. Initialize it with the first value (probably todos) and update it when you select another type correspondingly.
Next, pass this state data attribute to ModelsList component as a prop and simply filter the items within the list.
Introduce state to ModelosItems
constructor(props) {
super(props);
this.state = {currentGama: null};
}
on click update state
handleCheck(e) {
this.setState({
currentGama: e.currentTarget.dataset.gama
});
}
Add new prop to ModelosItems gama and feed currentGama state value to it
<ModelsList gama={this.state.gama} />
In your ModelList component, I'm guessing here
cars.filter(car => car.gama === this.props.gama).map(...
this will result in rendering only cars that have right gama. This does not use classnames to hide cars. But I think this is what you need.

Prevent Component/PureComponent with styled-components from re-rendering all items after state change

After adding styled-components to this example, we've noticed that our list component updates everything when only one item in state is modified.
The list rendering highlights (from React dev-tools) are excessive when adding/removing a single entry. One item is removed/added, then all items get highlighted.
code samples
github: https://github.com/chancesmith/reactjs-optimize-list-updates/tree/master/src
codesandbox: https://codesandbox.io/s/wn45qw44z5
Here is an example of the right list component (CategorizedList.js)
import styled from "styled-components";
const Item = styled.li`
color: #444;
`;
class CategorizedList extends PureComponent {
render() {
return (
<div>
{this.props.categories.map(category => (
<ul>
<li key={this.props.catStrings[category]}>
{this.props.catStrings[category]}
</li>
<ul>
{this.props.items.map((item, index) =>
item.category === category ? (
<div key={item.label + index}>
<Item>{item.label}</Item>
</div>
) : null
)}
</ul>
</ul>
))}
</div>
);
}
}
Preference
I'd prefer to use PureComponent so that shouldComponentUpdate() gets handled automatically.
Question
How can we make sure only the modified objects in items state are re-rendered?
If the data changes , the view will re-render. It shouldn't be an expensive process since it happens once on add/remove action. If you find performance issues it might be caused from something else.
In general this would be the way you can have some control on pure-components re-render:
https://reactjs.org/docs/react-api.html#reactmemo

Dynamically style a React element

I am trying to dynamically style a React element: In the nav bar, I would like to make the font color of the <div> displaying the current page to be different from the other <div>s representing other pages. I have passed into my <NavBar> component's props the params representing the current page.
From this information, I have figured out a way to achieve what I am trying to do: I store the <div>s representing the page links in an object and wrap the selected page in another <div> carrying a 'selected'class name.
However, I could envision scenarios where this solution would disrupt styling in place since it adds a wrapper element and classes within the wrapper element would take precedence over the 'selected' class.
Does anyone know a better way? I have posted my solution and the context below:
export default class NavBar extends React.Component {
constructor(props) {
super(props);
}
render() {
const pages = {
my_subscribed_docs:
<div className='nav__row2-item'>Documents I'm Subscribed To</div>,
my_created_docs:
<div className='nav__row2-item'>Documents I've Created</div>,
new_doc:
<div className='nav__row2-item'>
<div className="plus-icon">+</div>
<div className='nav__row2-text_left-of-icon'>New Document</div>
</div>,
};
const currentPage = this.props.path.slice(1);
pages[currentPage] =
<div className='nav__row2-item--selected'>{pages[currentPage]}</div>;
debugger;
return (
<nav>
<div className="nav__row1">
<div className="nav__logo">Worksheet Generator</div>
<div style={{cursor: 'pointer'}} onClick={this.props.logout}>
Logout
</div>
</div>
<div className="nav__row2" >
{ Object.values(pages).map( page => (
<div key={shortid.generate()}>{page}</div>)
) }
</div>
</nav>
);
}
}
If you want to dynamically add a class to your component, I recommend using classnames library. This is pretty much the way to do it when using external stylesheets.

Unknown props warning when passing props to children

I'm trying to create a reusable dropdown menu wrapper component using this pattern:
class DropdownMenu extends React.Component {
render() {
return (
<span>
{React.cloneElement(
this.props.children,
{
menuOpen: this.props.menuOpen,
toggleMenu: this.props.toggleMenu
}
)}
</span>
);
}
}
const HeaderUserDropdown = ({menuOpen, toggleMenu }) => (
<DropdownMenu>
<div className={menuOpen ? 'visible' : ''}>
<button onClick={toggleMenu} />
</div>
</DropdownMenu>
)
But I get an error along the lines of Warning: Unknown props menuOpen, toggleMenu on <div> tag. Remove these props from the element. I know that I can use data- to get this working correctly, but that seems sort of hacky. What's the correct way to pass these props down to the children?
React distinguishes between HTML elements which are written in lower case (e.g. <div>) and React components which start with a capital letter.
In your code, you're trying to clone an HTML div element and add the properties menuOpen and toggleMenu, but these attributes are not supported by <div>, hence the warning. You need to set custom attributes on an HTML element, you'll need to use the data- prefix convention.

onClick on div element won't pass target id

I have an issue in my React.js app with the target id not being passed if a onClick event is attached on a div element.
This is how I add the component:
<MenuOption title="Users" onOptionClick={this.onOptionClick}/>
This is the jsx-part of my MenuOption component:
<div id={title} className="row menu-option" onClick={onOptionClick}>
<p>{title}</p>
</div>
As you can see I'm simply assigning the prop title as the id and the onClick event.
Back in the parent component, implementation of the onClick method:
onSidebarOptionClick(e) {
console.log(e.target.id);
}
This does not seem to work since it always logs undefined.
However, if I add the id and the onClick event to my p element instead...
<div className="row menu-option">
<p id={title} onClick={onOptionClick}>{title}</p>
</div>
...it works just as expected, the correct id logs at every click.
So why is not the id passed if the click event is attached to a div?
I`m using ES6 and babel.
I should clarify that I'm using "dumb" components, declaring them like this:
const MenuOption = ({title, onOptionClick})
...which is why don't need to use {this.props.title} when using it.
Not sure as your Question missing constructor but try one of the following--
Method 1 (JSX):
<MenuOption title="Users" onOptionClick={this.onOptionClick.bind(this)}/>
Method 2 (es6):
constructor(props) {
super(props);
this.onOptionClick = this.onOptionClick.bind(this);
}
Method 3(es-next):
onSidebarOptionClick = (e) => {
console.log(e.target.id);
}
As you're passing onOptionClick as a prop to <MenuOption/>, it should be declared as this.porops.onOptionClick.
<MenuOption/> should look like this
<div id={title} className="row menu-option" onClick={thi.props.onOptionClick}>
<p>{title}</p>
</div>
Hope this helps!

Resources