Dynamically style a React element - reactjs

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.

Related

Is it possible to render a react class when given its name as a string?

Here's a smaller example of what I'm trying to do, I don't know if it's possible to do something similar or I should use an entirely different method.
import {Design1, Design2} from './page-designs';
let designs = {
"page1":"Design1",
"page2":"Design2",
"page3":"Design1",
"page4":"Design2"
}
class DesignedPage extends React.Component {
let Design = designs[this.props.page]
render(){
return(
<div className="row flex-fill d-flex">
<div className="col-1"></div>
<Design /* This is the line that fails */
data = {this.props.data}
/>
</div>
</div>
)}
}
class Main extends React.Component {
render(){
return(
<DesignedPage
page = {this.props.openPage} /*this could be any of page1-4 depending on button a click*/
data = {this.props.data}
/>
)}
}
Ideally this would render the react elements Design1 or Design2 based on what props.page is passed, but instead it returns
"Warning: <Design1 /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements." and "The tag <Design1> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter."
I've thought of making a long if, elseif, elseif.. statement in DesignedPage (the actual code has many more than 2 designs), which I'm fairly confident would work, but looks very messy in comparison.
You can't render the component name by getting its name as a string. You need to map the string to the component iteself:
let designs = {
"page1":Design1,
"page2":Design2,
}
If you pass a string, react would think it's a HTML tag, hence it say'Design1' tag is unrecognised. Also, you could import the components and use them as values in the designs object in place of strings.
let designs = {
"page1":Design1,
"page2":Design2,
"page3":Design1,
"page4":Design2
}
make one function that return react component..
getComponent = ({data, pageName}) => {
if(pageName === "page1") return <Desig1 />;
if(pageName === "page2") return <Design2 />;
}
and call function from render of DesignedPage component
const {page, data} = this.props;
return(
<div className="row flex-fill d-flex">
<div className="col-1">
{getComponent(page, data)}
</div>
</div>
)

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.

React/Redux Form ReRender Method

I would like to insert advertisement block (such as Google Adsense) inside of items list. I am using the react-redux & react-connect. Even if I need to refresh the feed and rerender, I would like to run the render of ad-block div only one time. Is there any way we can do this?
render(){
const { feed } = this.props;
return(
<div>
<div class="ad-block"><!-- Need To Render one time --></div>
<div class="items">
{_.map(feed.data, item => {
return <div class="item">.......</div>
})}
</div>
<div class="ad-block"><!-- Need To Render one time --></div>
);
}
How about to split it in 3 components?
export const Something = () => (
<>
<AdBlock>
<Feed>
<AdBLock>
<>
);
And connect Feed separately through Redux.

ReactJS- Only latest line of text is appearing in web page

I have this class function
class Fact extends React.Component {
render() {
return (
<img src="Logo.png"/>,
<center><p>FACT</p></center>,
<h1>Hello</h1>
)
}
}
export default Fact;
When running my localhost server, only "Hello" appears on the webpage. The logo doesn't appear, or the text message "FACT". Only "Hello" appears...How do I fix this?
Im trying to fit in 3 paragraphs as text to appear on my website, along with the logo. What's the best practice for this, rather then spam ?
You need to wrap the render output within a "root element", such as a <div> element to ensure that reactjs renders all the contents of the Fact component as you are expecting.
See the comments and code below, showing how to correct the error:
class Fact extends React.Component {
render() {
return (
<div> {/* Add <div> opening tag here */}
<img src="Logo.png"/>,
<center><p>FACT</p></center>,
<h1>Hello</h1>
</div> {/* Add </div> closing tag here */}
)
}
}
Note also that </h1> is missing the > in your question. This has been corrected in my answer
In React, if you're rendering multiple nodes (e.g. img, center, h1), you need to nest them under one parent node, e.g. <div>. That is, you can only return one top level node from your render function.
So:
class Fact extends React.Component {
render() {
return (
<React.Fragment>
<img src="Logo.png" /> ,
<center>
<p>FACT</p>
</center>
<h1>Hello</h1>
</React.Fragment>
);
}
}
Note that React.Fragment could also be something like a div.

How does the ReactBootstrap OverlayTrigger container property work?

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>

Resources