I'm trying to build a navbar, using React and hooks, where each div will change to a specific color on an onMouseEnter and onMouseLeave. I can't figure out why they're all affected if i hover over one. I guess I'm asking how I could make them independent of one another.
Sorry if this is a really obvious mistake. Still really green. Thanks again!
Here is a link to the CodeSandbox: https://codesandbox.io/s/holy-snowflake-twojb?file=/src/navbar.js
I refactored your code.
You can check it in https://codesandbox.io/s/lucid-lake-u3oox?file=/src/navbar.js
You are using the function setBackground to set the background color in the hoverStyle CSS class which affects to all <div className="hoverStyle"> tags.
There are many options to do that. If you want to do it with React, one way of do it is creating a CSS class like this:
.active {
background-color: #ffac4e;
}
then, create a functional component
const Activable = ({ className, children, bgColor}) => {
const [active, setActive] = useState('#fff');
return (
<div className={`${ className } ${ active }`
onMouseEnter = {() => setActive( bgColor )}
onMouseLeave = {() => setActive('#fff')}
>
{ children }
</div>
)
}
then replace your ` with this new component like this:
<Activable className="hoverStyle" bgColor="#ffac4e">
<div style = {navChildLeft}>2020</div>
<div style = {navChildTop}>
Shy RL <br />
Digital - Album art
</div>
</Activable>
and repeat with the rest of <div>'s
With react, it is important to think about reusable components. How can you create something that you can reuse over and over again in different parts of your project.
Take a look at this sample of your project broken into components.
https://codesandbox.io/s/holy-brook-3jror?fontsize=14&hidenavigation=1&theme=dark
I would recommend reading this to help out: https://reactjs.org/docs/thinking-in-react.html
Related
Why does defining a React functional component inside another functional component break CSS transitions?
function Doohick({isOpen}: {isOpen: boolean}) {
const style = {
transition: 'opacity 2s ease',
...(isOpen ? {opacity: 1} : {opacity: 0})
}
return (
<div style={style}>
Doohick!!!
</div>
)
}
function Parent() {
const [open, isOpen] = useState(false)
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Doohick</button>
<Doohick isOpen={isOpen} />
</>
)
}
If I define Doohick outside of Parent, as above, everything works great. If I move the definition inside Parent, with no other changes, my CSS transitions break. Other CSS properties are fine.
Why does defining a functional component inside another functional component break CSS transitions?
Complicated Explanation of Why I Want To Do This
I hear you asking: why would I want to do that? I'll tell you, but bear in mind you don't need to know any of this to understand the specific problem.
I want to encapsulate the Doohick state in a custom hook:
function useDoohick() {
const [isOpen, setIsOpen] = useState(false)
const ToggleButton =
<Button onClick={() => setIsOpen(!isOpen)}>Toggle Doohick</Button>
const Doohick = <MyDoohick show={isOpen}/>
return {ToggleButton, Doohick}
}
function Parent() {
const {Doohick, ToggleButton} = useDoohick()
return (
<>
{ToggleButton}
{Doohick}
</>
)
}
But I also want the Parent to be able to pass its own props into Doohick or ToggleButton. I can almost achieve that that like this:
function useDoohick() {
const [isOpen, setIsOpen] = useState(false)
const ToggleButton = ({text}) =>
<Button
onClick={() => setIsOpen(!isOpen)}
>
{text}
</Button>
const Doohick = () =>
<MyDoohick show={isOpen} />
return {ToggleButton, Doohick}
}
function Parent() {
return (
<>
<ToggleButton text='Burninate' />
<Doohick />
</>
)
}
This works as advertised: ToggleButton renders with the expected label and controls whether or not Doohick is shown. But this pattern breaks some CSS styles (specifically, transitions) I have defined on Doohick. Other styles are fine.
I can still call it like this:
function Parent() {
return (
<>
{ToggleButton({text: 'Burninate'})}
{Doohick()}
</>
)
}
...and the transitions work correctly. But I would much prefer the standard JSX syntax here:
<ToggleButton text='Burninate />
Clearly, <Doohick /> and Doohick() are different. But what is it about the former that breaks CSS transitions here?
The root of the problem boils down to defining the custom components inside the Parent. The hook itself is irrelevant. But this pattern of encapsulating state in a custom hook while returning a customizable component is really powerful and almost works, so I'm hoping there's a way it can be saved.
TL;DR
Why does defining a component within another component break my CSS transitions (and possibly other styles I haven't found yet)? How can I get around this while still calling my nested component with JSX-style syntax?
Defining a component inside another component will always result in issues like this. Every time the outer component renders, you create a brand new definition of the inner component. It may have the same text as the one from the previous render, but it's a different function in memory, so as far as react can tell it's a different type of component.
The component type is the main thing that react looks for when reconciling changes. Since the type changed, react is forced to unmount the old component and then mount the new one. So rather than having a <div> on the page who's style is changing, you have a div with some style, then it gets deleted and an unrelated div gets put onto the page. It may have a different style, but since this is a brand new div, the transition property won't do anything.
So I tried creating a simple navbar that would have its buttons controlled by useState. However I have a problem where the button icon color wont update even though the state of the variable that controls it changes.
Now, I did some testing and and added text into the icon component (not show here) and made it so it was controlled by the same state as the color on the icon is now. And for some reason when I did that the state and the text inside the component both changed correctly. Could anyone provide an explanation on why that happens? Because to me it seems like I've misunderstood how react binds things to states and controls them.
Navigation bar component
import NavButton from "./NavButton"
import { useState } from "react";
function NavBar(){
const [buttons, setButtons] = useState([
{id:1, name:"Orders", icon:"bx:bx-dollar-circle", active:false},
{id:2, name:"Menu", icon:"ic:round-restaurant-menu", active:false},
{id:3, name:"Leave", icon:"uil:exit", active:false}
]);
const toggleButton = (id) => {
setButtons(buttons.map(button => (
button.id === id ? {...button, active:!button.active} : {...button, active:false}
)))
}
return (
<div className="h-1/6 bg-white border-b-lebo flex flex-row justify-around">
<>
{buttons.map((button) => (<NavButton button={button} key={button.id} onToggle={toggleButton}/>))}
</>
</div>
)
}
export default NavBar;
Navigation button component
import Icon from "./Icon";
function NavButton({button, onToggle}){
return (
<button onClick={() => onToggle(button.id)} className={`font-bold text-gray-500 flex flex-col items-center justify-center flex-grow w-5 hover:bg-gray-100`}>
<p className="self-center">{button.name}</p>
<Icon icon={button.icon} name={button.name} color={button.active ? "#454545" : "#8b8b8b"}/>
</button>
)
}
export default NavButton;
Icon component
function Icon({icon, color, name}) {
return (
<div>
<span color={color} className="iconify h-10 w-auto self-center" data-icon={icon}></span>
</div>
)
}
export default Icon
I solved my problem by creating 2 different Icon components.
Icon and IconDark and conditionally rendering them inside the NavButton component.
Not sure if it is the "correct" way of doing things but it got the job done.
I'm going to guess the reason why it didn't render the colors correctly earlier is because of the attribute "color" inside the component. I think JSX just took it in as another prop and did nothing with it after the first render of the element.
edit 1: nvm it definitely didn't get the job done. At least not well enough. The icon swap in the render isn't fast enough so it causes the user to see the icon swap.
edit 2: This article held the answer that I needed.
https://dev.to/abachi/how-to-change-svg-s-color-in-react-42g2
It turns out that to change an svg color with react you need to set the initial fill (or for me color) value inside the svg component to "current" and then pass the real value in from the parent element conditionally.
Long story short - Controlling SVG values is a little different to controlling text values in react.
Hi I have a component like this :
const Comp = (props) => {
return(
<div>
data here
</div>
)
}
and I want to use framer motion like this:
const Container = () => {
return(
<motion.Comp variants={variants} someProp="someProp"/>
)
}
The reason I want to do this is that I don't think making a lot of div as a wrapper is a great idea.
I could use motion.div in "Comp" component but some of my app component is made that I don't need animating all of them in my whole app. I just want a solution to add animations to the first element in the component.
Also I searched and I found a solution "motion(Comp)", but it's not working.
From the docs (https://www.framer.com/docs/component/#custom-components), you can wrap any component with motion() to use it as if it were a framer-motion component. You mentioned that you found that solution but that it's not working, and without any more details about it I can't give you a specific reason why. But most likely you're forgetting to pass the ref through to your component, like this:
const Foo = React.forwardRef((props, ref) => <div ref={ref} />)
The ref is the way that framer-motion will identify which element on the page is the one it should be animating, so it needs to be passed to the correct element. Once that's done, you can wrap it with motion:
const MotionFoo = motion(Foo)
Keep in mind that regardless of how many elements your custom component has, only the one that has the ref passed to it will be animated. For example:
const BusyFoo = React.forwardRef((props, ref) => (
<div>
<div ref={ref}>I will be animated!</div>
<div>I won't be animated. :(</div>
</div>
)
I am trying to create a website by using GatsbyJS, and I got stuck whenever I need to set a onClick event to toggle a class in one of my components. As a beginner with react and gatsby, I'm having a hard time to do it.
So essentially I want to make the following JS code in React/GatsbyJS:
const hamburger = document.querySelector('.burger_menur');
hamburger.addEventListener('click', function(){
this.classList.toggle('open');
});
The following code is my current code in Gatsby component. Have to say, I am using GSAP to make an animation.
This is my code:
import React from 'react';
import { Tween, Timeline } from 'react-gsap';
import '../styles/burger.scss'
const Burger = () => (
<Timeline
target={
<div className='burger'>
<div className='burger_menu'>
<div className='bar half start'></div>
<div className='bar'></div>
<div className='bar half end'></div>
</div>
</div>
}
>
<Tween from={{ opacity: '0', marginRight: '0rem' }} to={{ opacity: '1', marginRight: '5rem' }} ease="Back.easeOut" delay={2}/>
</Timeline>
);
export default Burger
Hopefully someone can help me out with this.
If you're going to go with React, don't manipulate the DOM directly.
More specifically, don't try to act directly on any part of the DOM generated by React.
Here, you're using plain DOM manipulation to attach your event to elements generated by React (also, there's a typo in your class name):
const hamburger = document.querySelector('.burger_menur');
hamburger.addEventListener('click', function(){
this.classList.toggle('open');
});
The thing is, while it may sometimes work, React will regenerate new elements for your menu when it deems it necessary, and your events listeners will be lost.
You have to do it "the React way":
...
<div className='burger'>
<div className={`burger_menu ${this.state.isOpen? ' open' : ''}`} onClick={() => this.setState({ isOpen: !isOpen })}>
<div className='bar half start'></div>
<div className='bar'></div>
<div className='bar half end'></div>
</div>
</div>
...
Don't forget to initialize your state with { isOpen: false }
Clicking the div will toggle this.state.isOpen, which is used to decide whether the class-name will be 'burger_menu' or 'burger_menu open'.
Note: There are more elegant ways to work with classlists when they get longer, but your component being simple and for the sake of clarity, a string template will more than do.
If any of this sounds confusing, please read through the official tutorial Intro To React, it's very well explained and covers everything needed here.
If you're already comfortable with this and want to know more about handling events in React, the docs has you covered once again: Handling Events
In App.css, I have
.theme {
color: green;
}
And I have className="theme" scattered in multiple components.
Is there a way to change the theme color from green to blue on an event?
If not, how should I design my code?
Well, You can create 2 classes named .blue-theme and .green-theme
Whenever, some event occurs,
onClick = (themeCol) => {
this.setState({theme:thmeCol})
}
render(){
return(
<button onClick={()=>onClick('blue-theme')}>Blue theme</button>
<button onClick={()=>onClick('green-theme')}>Green theme</button>
<div className={this.state.theme}> Sample </div>
)
}
You can pass the value of theme.
you can try
const themeClass = "";
if (event-as-wanted){
themeClass="theme";
}
className={themeClass}
also you can use style insted of className in same file
const theme ={
color: '';
};
style={theme}
and change it with events like
if (event-as-wanted){
theme.color = "green";
}
You can conditionally render the <style/> tag to override style definition for the class in the whole document.
class App extends React.Component {
state = {
red: true,
}
render() {
return (
<div>
<div className="foo">Foo</div>
<button onClick={() => this.setState({ red: !this.state.red })}>
Toggle Red
</button>
{this.state.red && <style>{".foo { color: red }"}</style>}
</div>
);
}
}
Keep in mind that inside JSX tags, curly brackets will be picked up by the interpreter and may break the parser. To avoid that, you should put your CSS inside a string like in the example above.
Adding a <style/> tag to CSS document will override any equally specific CSS rules that came before that. Once the condition is no longer met, the style tag will be removed and the original styling will be restored.
in react.js just set the state of color to whatever and on a click event toggle the color
class App extends React.Component {
constructor(props) {
super(props);
this.state = {color: green};
this.changeColor = this.changeColor.bind(this);
}
changeColor() {
const newcolor = this.state.color == green ? blue : green;
this.setState({ color: newcolor});
}
render() {
return (
<div class="theme" style={{color: this.statecolor}}>
<button onClick={this.changeColor}>
</button>
//put all html withing the parent DOM of element with class theme accordingly or it wont render.
</div>
);
}
Make two class, .green-theme{
color:'green'} and similarly, blue theme.
Mantain a REDUX STATE, CURRENT_THEME. Upon event fire, change the redux state accordingly and everywhere, where you want to use CURRENT_THEME, use it using mapStateToProps.
I would rather try to use almost pure CSS solution:
in App.css
#App.first-theme .theme { color:green; }
#App.second-theme .theme { color:blue; }
in App's render:
<div id="App" className={this.state.currentTheme}>
<AnotherComponent1 />
<AnotherComponent2 />
</div>
All you need to do is to change this.state.currentTheme appropriately. You can even use prop injected from the redux.
Almost all other solutions posted here have the same flaw: you have to adapt all your components to use the theme. Using this solution, you are able to change app's appearance without additional code in your components.
Trust me, injecting the same property from redux store/react context for every component will give you headaches and a lot of unnecessary code.
You should also try to avoid generating additional <style> tags - you will end up having plenty of !important and HTML, logic, and CSS in one file. What a mess! Imagine, what would happen if you would like to use SCSS in the future...