React Spring and Styled Components - reactjs

I'm new to React, trying to integrate some animations. I'm using Gatsby.js framework.
const LeadHeading = styled(animated.h1)`
font-weight: bold;
font-size: 3rem;
font-family: ${props => props.theme.fontSecondary};
margin-bottom: 0;
line-height: 2.5rem;
color: #FFF3D8;
const IndexPage = ({menuOpen}) => {
const h1Animation = useSpring({
opacity: menuOpen ? '0' : '1'
})
return (
<>
<Layout>
<Section className="hero is-fullheight">
<div className="hero-body container is-flex">
<LeadHeading
style={h1Animation}
>
some heading
</LeadHeading>
</div>
</Section>
</Layout>
</>
)
}
export default IndexPage
The menu status is managed in the "layout" component via the useState hook.
const [menuOpen, setMenuOpen] = useState(false)
Basically, I just want to fadeout the h1 when the menu pops up, since it doesn't look good due to the transparency of the menu.
Appreciate your help.

const LeadHeading = styled(animated.h1)`
font-weight: bold;
font-size: 3rem;
font-family: ${props => props.theme.fontSecondary};
margin-bottom: 0;
line-height: 2.5rem;
color: #FFF3D8;
`
const IndexPage = ({menuOpen}) => {
const h1Animation = useSpring({
opacity: menuOpen ? '0' : '1'
})
return (
<>
<Layout>
<Section className="hero is-fullheight">
<div className="hero-body container is-flex">
<LeadHeading
style={h1Animation}
>
some heading....
</LeadHeading>
</div>
</Section>
</Layout>
</>
)
}
export default IndexPage
EDIT// The menu status is managed in the "layout" component via useState hook.
But it should be possible, to pass the data to "IndexPage" right?
Ok I thought I can skip the animated part in the render method, since I use it above in the styled component variable. (sry I forgot to mention that in my previous post).
Your approach was one of my first approaches but it didnt work.

Related

How to change a component's color with styled-components?

I want to change a component Area's background color when move link hover on it.
import Link from "next/link";
...
export const ShowField: React.FC<ShowFieldProps> = ({ link, name }) => {
return (
<Link href={link}>
<YellowLink>
<Area backgroundColor="blue">
<Area>
<Name text={name} />
</Area>
</Area>
</YellowLink>
</Link>
);
};
const YellowLink = styled.div`
&:hover ${Area} {
background-color: yellow;
}
`;
Area is a self-created component. There is some errors if use it in the styled.div:
No overload matches this call.
Overload 1 of 3, '(first: TemplateStringsArray | CSSObject |
If use this way
const YellowLink = styled.div`
a {
&:hover {
background-color: yellow;
}
}
`
There happens nothing on the page. How to haddle <Area backgroundColor="blue"> and change color for it?
Without seeing more of the code for the other components, this may not work for you. One approach you could take is styling a Link for your YellowLink component where you define the styles of that link.
For example:
// Create a styled link component...
const StyledLink = styled(Link)`
background-color: ${(props) => props.backgroundColor};
// Add any additional styles you need for the link
&:hover {
background-color: yellow;
}
`;
// Usage
return (
<StyledLink href={link} backgroundColor="blue">
<Name text={name} />
</StyledLink>
);

React paginate layout issue

I'm new with react and I'm trying to adapt my pagination to the design.
I'm not sure the react-paginate is the right one to use however I don't have much more expertise.
I managed to have something working but only with CSS, I don't like the hack approach.
is it possible to add Result found?
and page 1 of whatever?
my design should look like this:
But this is what I have at the moment:
And finally this is my react code:
<div className='paginate'>
<span className='result-found'>Result found</span>
<ReactPaginate
previousLabel="Back"
previousClassName="prev fa fa-caret-left"
nextLabel="Next"
nextClassName="next fa fa-caret-right"
breakLabel="..."
pageLinkClassName="page-link"
pageClassName="page"
breakClassName={""}
pageCount={total}
marginPagesDisplayed={1}
pageRangeDisplayed={4}
onPageChange={handlePageClick}
containerClassName="pagination"
subContainerClassName="pages pagination"
activeClassName="active"
/>
</div>
Here you go:
It is 90% CSS and 10% React. :D Edit the CSS for perfection. The code for the screenshot looks like this:
import { useEffect, useState } from "react";
import "./styles.css";
const PAGE_SIZE = 10;
export default function App() {
const [total, setTotal] = useState(42);
const [pageNo, setPageNo] = useState(0);
const maxPages = Math.ceil(total / PAGE_SIZE);
useEffect(() => {
// do your async call here
// set total
}, [pageNo]);
// take care of boundary condition like last and first pages
const onNext = () => pageNo < maxPages - 1 && setPageNo(pageNo + 1);
const onPrev = () => pageNo > 0 && setPageNo(pageNo - 1);
return (
<div className="App">
<div className="pagination-container">
<div className="pagination-left">
<span>{total} results found</span>
</div>
<div className="pagination-right">
<div className="pagination-current">
Page {pageNo + 1} of {maxPages}
</div>
<div className="pagination-buttons">
<div className="pagination-button-wrapper">
<button onClick={onPrev} className="pagination-button">
<
</button>
</div>
</div>
<div className="pagination-buttons">
<div className="pagination-button-wrapper">
<button onClick={onNext} className="pagination-button">
>
</button>
</div>
</div>
</div>
</div>
</div>
);
}
You can do usability enhancement by setting the buttons disabled={true} when the boundary condition reaches on first and last pages.
And the CSS is:
.pagination-container {
background-color: black;
display: flex;
justify-content: space-between;
padding: 1rem;
color: white;
}
.pagination-left {
display: flex;
align-items: center;
}
.pagination-right {
display: flex;
align-items: center;
}
.pagination-buttons {
display: flex;
margin-left: 0.5rem;
}
.pagination-buttons > * {
border: 1px solid white;
padding: 0.1rem 0.5rem;
}
.pagination-button {
background-color: transparent;
color: white;
border: 0;
}
Here also, you can edit CSS to match the exact style with arrows and buttons, and margins and paddings, and borders.
And a functional demo is here: https://codesandbox.io/s/zealous-ardinghelli-y4txu?file=/src/App.js
Larger Context: I assume you may want to reuse this component on multiple places. If so, you may want to extract out the logic into props. That means get rid of states and specialized logic (fetch data, in this case) to make it a stateless component by editing this component something like this:
interface Props {
onNext: () => void;
onPrev: () => void;
pageSize: number;
totalItems: number
}
export const PaginationBar = (props: Props) => {
// same code as above but no useEffect or useState
}
Now you have your own flexible pagination component. See here: https://codesandbox.io/s/eager-hermann-pjdt3?file=/src/App.js
You use it like this:
<Pagination
onNext={onNext}
onPrev={onPrev}
total={total}
pageNo={pageNo}
pageSize={10} />
Hope this helps!
React pagination come with minimum styling , anytime you can import demo css from github page
https://github.com/AdeleD/react-paginate/tree/master/demo/styles
in your component or
creating a styled component and customize your pagination all classes already include in your HTML like .active .first .last
or you can create custome styles for that class and append on your main.css

React do I need a hook for each component?

Problem Description
I have faced this issue multiple times and am not sure what is the best approach. Say I have 3+ components how can I have a state tracker for each of them? Do I need n useState() hooks for n components?
In this example, I want to change the image displayed with onMouseOver(). The way I did it, the hook state is applied to all the images, which is not what I desire to do.
Restrictions
I want to have eventually a custom drop-down menu with onClick() particular to the image folder. So in that sense, I need an individual state tracker for each component?
Code and Snippet
const openFolder = "https://i.postimg.cc/V6L7kBCV/folder-Open.png";
const closedFolder = "https://i.postimg.cc/hG0yn3fm/folder-Closed.png"
function App() {
const [image, setImage] = React.useState(closedFolder);
const handleMouseOver = () => {
setImage(openFolder);
}
const handleMouseLeave = () => {
setImage(closedFolder);
}
return (
<div className="window">
<div className="slider">
<div className="container">
{buildLatexFolder(image,handleMouseOver,handleMouseLeave)}
</div>
</div>
</div>
);
}
const buildLatexFolder = (image,handleMouseOver,handleMouseLeave) => {
const courseNames = [
'A', 'B', 'C', 'D', 'E', 'F'
//and many more other
]
var folders = courseNames.map((value, index) => {
return (
<div className="folderContainer">
<span>{value}</span>
<div onMouseEnter={() => {handleMouseOver()}} onMouseLeave={() => {handleMouseLeave()}}>
<img src={image} width="130"/>
</div>
</div>
);
});
return folders;
}
ReactDOM.render(<App />, document.querySelector("#app"));
.window {
width: 500px;
height: 130px;
display: flex;
align-items: center;
margin-left: auto;
margin-right: auto;
}
.slider {
border-radius: 0px 0px 16px 16px;
background-color: #1F1F1F;
color: #ffffff;
overflow: hidden;
float: right;
}
.container {
display: flex;
width: 700px;
height: 130px;
margin-top: 10px;
align-items: center;
text-align: center;
}
.folderElement {
margin-left: 12px;
margin-right: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
with JSFiddle here
What I Have Tried
Previously, when faced with this problem I would just make, say, 3 hooks for my 3 components and handle the logic this way. But now I have a large number of components and don't know what is the best approach.
I have searched online 'React Should I have a hook for each component', 'How many hooks to have for components', and so on and so forth but the search results are inaccurate with what I am trying to find, or for that matter my question is inaccurate. I don't know how to proceed.
Does anyone know how I can fix this issue?
You need to have component modularization in your application. Then you can have a single CourseFolder which can be used as a child component resides in your parent component which is App. Using map, you can view multiple child components having different courseDetails inside them.
Refer the following code-snippets I created to solve your issue.
App.js
import React from "react";
import CourseFolder from "./CourseFolder";
// import "./folderStyles.css"; /* kept the styles posted in the question */
const courseNames = [
"A",
"B",
"C",
"D",
"E",
"F",
//and many more other
];
function App() {
return (
<div className="window">
<div className="slider">
<div className="container">
{courseNames.map((value, index) => (
<CourseFolder value={value} key={index} />
))}
</div>
</div>
</div>
);
}
export default App;
Then create the child component as follows.
CourseFolder.js
import React from "react";
const openFolder = "https://i.postimg.cc/V6L7kBCV/folder-Open.png";
const closedFolder = "https://i.postimg.cc/hG0yn3fm/folder-Closed.png";
function CourseFolder(props) {
const { value, key } = props;
const [image, setImage] = React.useState(closedFolder);
const handleMouseOver = () => {
setImage(openFolder);
};
const handleMouseLeave = () => {
setImage(closedFolder);
};
return (
<div className="folderContainer" key={key}>
<span>{value}</span>
<div
onMouseEnter={() => {
handleMouseOver();
}}
onMouseLeave={() => {
handleMouseLeave();
}}
>
<img src={image} width="130" alt="alternative" />
</div>
</div>
);
}
export default CourseFolder;
Hope this would be helpful to solve your issue.

Converting CSS to styled component issue

I am new to styled components. In my React code I have some conditional rendering to change some CSS depending on if I scroll my navbar with the following code:
const [colorChange, setColorchange] = useState(false)
const changeNavbarColor = () => {
if (window.scrollY >= 80) {
setColorchange(true)
} else {
setColorchange(false)
}
}
window.addEventListener("scroll", changeNavbarColor)
<nav className={colorChange ? "navbar colorChange" : "navbar"}>content</nav>
My problem is to write the styled component for this to work.
My normal CSS looked like this:
.navbar {
/*styles...*/
}
.navbar.colorChange {
/*styles...*/
}
I started by creating the following with some styles:
const Navbar = styled.nav`
/*styles...*/
`
But how do I share the styles that are common for both navbar and colorChange; I tried appending the following to the NavBar styled component:
const Navbar = styled.nav`
/*styles...*/
.colorChange{
/*styles...*/
}
`
And then when rendering the component in React, how do I convert this line to use the styled components instead?
<nav className={colorChange ? "navbar colorChange" : "navbar"}>content</nav>
You need pass colorChange state to NavBar component.
This is NavBar declaration
const NavBar = styled.nav`
//styled for nav bar
color: ${props => props.color}
`
And use it in component
<NavBar color={colorChange ? '#fff':'#ddd'}>{children}</NavBar>
Just pass prop as dynamic value
const Navbar = styled.nav`
// navbar common css
color: ${({ color}) => color};
`
<Navbar color={colorChange ? 'red':'transparent'} />
as you commented you have multiple styles I suggest below approach just pass on prop for isColorChange
const Navbar = styled.nav`
font-size: 13px; //common style
background: ${props => (props.isColorChange ? "#6495ED" : "#2b2b2b")};
background-color: ${props => (props.isColorChange ? "#6495ED" : "#2b2b2b")};
margin-top: ${props => (props.isColorChange ? "10px" : "20px")};
padding-top: ${props => (props.isColorChange ? "5px" : "10px")};
padding-bottom: ${props => (props.isColorChange ? "5px" : "10px")};
`;
<Navbar isColorChange={true / false}></Navbar>;

Styled-components: Styles are not applied when trying to style already styled component

I'm trying to style my component which was styled already. But styles in the new rules are not applied in output CSS.
Can I style component that I already styled?
Thanks you in advance for your help.
EDIT: Add rest of LanugageChooser definition
// COMPONENT THAT I'M TRYING TO STYLE
const LanguageChooser = () => {
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
return (
<Container>
<Flag { ...languages.pl }/>
<Flag { ...languages.en }/>
</Container>
)
}
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: .2s all;
&:hover {
font-size: 36px;
}
`;
return (
<Button onClick={ () => i18n.changeLanguage(language) }>{ flag }</Button>
)
}
// TRYING ADD MARGIN LEFT, BUT THERE IS NO RESULT.
// ANY OTHER CSS PROPERTY ARE NOT APPLYING
const Navbar = ({ color }) => {
...
const StyledLanguageChooser = styled(LanguageChooser)`
margin-left: auto;
`;
const Nav = styled.nav`
display: flex;
align-content:center;
background: ${ color };
padding: 2px 3px;
`;
return (
<Nav className="navbar">
<StyledNavLink to="/home">...</StyledNavLink>
<StyledNavLink to="/maps">...</StyledNavLink>
<StyledNavLink to="/charts">...</StyledNavLink>
<StyledLanguageChooser/>
</Nav>
)
}
First, move the styled component outside of function scope or your style will reset on every render and you will get heavy performance issues.
Secondly, in order to apply styles, you need to pass className property.
See Styling normal React components
If you use the styled(MyComponent) notation and MyComponent does not render the passed-in className prop, then no styles will be applied.
const Container = styled.div`
display: flex;
align-items: center;
height: 36px;
& > div:not(:last-child) {
margin-right: 5px;
}
`;
const LanguageChooser = ({ className }) => {
return (
<Container className={className}>
<Flag {...languages.pl} />
<Flag {...languages.en} />
</Container>
);
};
const Button = styled.div`
cursor: pointer;
font-size: 24px;
transition: 0.2s all;
&:hover {
font-size: 36px;
}
`;
const Flag = ({ flag, language }) => {
const { i18n } = useTranslation();
return <Button onClick={() => i18n.changeLanguage(language)}>{flag}</Button>;
};

Resources