I want to run an animation using transition. The problem is I am using state to control it. So now, the components are created in render method. But every time the state is changed, the component is re-rendered. So how can I create the smaller components, so they are in scope, and I can use state to animate them?
import React, {Component, Fragment} from 'react';
import ArrowTemplate from "./ArrowTemplate";
import styled from 'styled-components';
class Accordion extends Component {
constructor(props) {
super(props);
this.state = {isAccordionExpanded: false};
this.toggleAccordion = this.toggleAccordion.bind(this);
}
toggleAccordion() {
this.setState({isAccordionExpanded: !this.state.isAccordionExpanded})
}
render() {
const {isAccordionExpanded} = this.state;
const AccordionSection = styled.div`
display: flex;
flex-direction: column;
`;
const AccordionBtn = styled.button`
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
display: flex;
align-items: center;
border: none;
outline: none;
transition: background-color 0.6s ease;
:hover,
:focus,
:active {
background-color: #ccc;
}
`;
const P = styled.p`
font-family: "Open Sans", sans-serif;
font-weight: 600;
font-size: 14px;
`;
const AccordionContent = styled.div`
background-color: white;
${isAccordionExpanded === true ? `height: 100px` : `height: 0`};
overflow: hidden;
transition: max-height 0.6s ease;
`;
const AccordionText = styled.div`
font-family: "Open Sans", sans-serif;
font-weight: 400;
font-size: 14px;
padding: 18px;
`;
return (
<AccordionSection>
<AccordionBtn
onClick={this.toggleAccordion}
>
<P>
<ArrowTemplate
color={"black"}
direction={"down"}
aria={"aria-roles: 'button'"}
onClick={this.toggleAccordion}
/>
</P>
</AccordionBtn>
<AccordionContent>
<AccordionText>
test
</AccordionText>
</AccordionContent>
</AccordionSection>
)
}
}
export default Accordion;
ArrowTemplate
import React from "react";
import BlackDownArrowSVG from './svgs/black-down-arrow.svg';
import WhiteDownArrowSVG from './svgs/white-down-arrow.svg';
import styled from 'styled-components';
import PropTypes from 'prop-types';
ArrowTemplate.propTypes = {
color: PropTypes.string.isRequired,
direction: PropTypes.string.isRequired,
styles: PropTypes.string,
aria: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};
function ArrowTemplate(props) {
const {color, direction, styles, aria, onClick} = props;
const StyledArrowTemplate = styled.img.attrs({
src: color.toLowerCase() === "black" ? BlackDownArrowSVG : WhiteDownArrowSVG,
aria,
})`
${direction.toLowerCase() === "up" ? "transform: rotate(180deg);" : ""}
${styles}
`;
return <StyledArrowTemplate onClick={onClick}/>;
}
export default ArrowTemplate;
Try using
direction={this.state.isAccordionExpanded ? 'up' : 'down'}
for <ArrowTemplate />
I managed to do this by passing the properties (state) to a styled component like so:
<AccordionContent
ref={content}
isAccordionExpanded={isAccordionExpanded}
height={content.current === null ? '0' : content.current.scrollHeight}
>
const AccordionContent = styled.div`
max-height: ${({ isAccordionExpanded, height }) => (isAccordionExpanded ? height : '0')}px;
background-color: red;
overflow: hidden;
transition: max-height 0.7s;
`;
Related
What I did:
I'm passing some props to functional component Stat.jsx.
What I expected:
I need to pass some background gradient color codes as a string type prop to the Stat.jsx component to make custom color elements.
What happened:
Props aren't passing to the Stat.jsx, also props object is empty.
Stat.jsx
import React from 'react';
import styled from 'styled-components';
const Stat = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 0 2.5em;
width: auto;
height: 2.5em;
border-radius: 0.5em;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2);
background: linear-gradient(160deg, ${(props) => console.log(props) });
font-size: 1.8em;
font-family: Rubik-Medium;
color: #fff;
`;
// console.log(props) is returning object: { children: "1000", theme: {} }
export default ({ value }) => <Stat>{value}</Stat>;
Stats.jsx
import React from 'react';
import Stat from './Stat';
import styled from 'styled-components';
const Stats = styled.div`
display: flex;
`;
export default () => (
<div>
<Stats>
<Stat value="1000" background="#F4D03F, #16A085" />
</Stats>
</div>
);
Quick Fix
Because you don't pass the background prop to the actual Stat component:
export default (props) => <Stat {...props}>{props.value}</Stat>;
Explanation
A better way to illustrate the issue is by renaming your components:
import React from 'react';
import styled from 'styled-components';
const StyledStat = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 0 2.5em;
width: auto;
height: 2.5em;
border-radius: 0.5em;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2);
background: linear-gradient(160deg, ${(props) => console.log(props) });
font-size: 1.8em;
font-family: Rubik-Medium;
color: #fff;
`;
export default function Stat(props){
const { value } = props;
return (
<StyledStat {...props}>
{value}
</StyledStat>;
};
Styled components props comes from the ThemeProvider usually, this is why you see a theme props while console.logging inside your styled.div
Usually in App.js you have something like that:
// src/App.jsx
import React from 'react'
import { ThemeProvider } from 'styled-components';
const theme: {
colors: {
primary: blue,
}
}
const App = () => (
<ThemeProvider theme={theme}>
<Stat />
</ThemeProvider>
)
export default App;
you can access these attributes with
${(props) => props.theme.colors.primary }
because styled-components provides its theme props to every StyledComponents (there is a Context Provider/consumer thing behind)
import React from 'react';
import styled from 'styled-components';
const Stat = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 0 2.5em;
width: auto;
height: 2.5em;
border-radius: 0.5em;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2);
background: linear-gradient(160deg, ${(props) => props.theme.colors.primary} });
font-size: 1.8em;
font-family: Rubik-Medium;
color: #fff;
`;
Hello guys I been looking in the internet trying to apply defaultProps to my components but nothing is working atm.
I will show you my code:
import styled from "styled-components";
const Button = styled.button`
text-transform: uppercase;
font-weight: bold;
font-size: 12px;
padding: 10px 20px;
border-radius: 5px;
border-style: none;
:focus {
outline: none;
}
${props =>
props.primary &&
`
background-color: ${props.theme.colors.primary};
color: ${props.theme.colors.blank};
:hover {
background-color: ${props.theme.colors.blank};
color: ${props.theme.colors.primary};
border-style: solid;
border-color: ${props.theme.colors.primary}
}
`}
${props =>
props.secondary &&
`
background-color: ${props.theme.colors.secondary};
color: ${props.theme.colors.blank};
:hover {
background-color: ${props.theme.colors.blank};
color: ${props.theme.colors.secondary};
border-style: solid;
border-color: ${props.theme.colors.secondary}
}
`}
${props =>
props.success &&
`
background-color: ${props.theme.colors.success};
color: ${props.theme.colors.blank};
:hover {
background-color: ${props.theme.colors.blank};
color: ${props.theme.colors.success};
border-style: solid;
border-color: ${props.theme.colors.success}
}
`}
${props =>
props.warning &&
`
background-color: ${props.theme.colors.warning};
color: ${props.theme.colors.blank};
:hover {
background-color: ${props.theme.colors.blank};
color: ${props.theme.colors.warning};
border-style: solid;
border-color: ${props.theme.colors.warning}
}
`}
`;
export default Button;
I just want to setup defaultProps to give some styled by default if I dont do for example:
<Button primary>Text</>
Here is another example, this is a bit different:
import React from "react";
import styled from "styled-components";
const Wrap = styled.input`
${props =>
props.primary &&
`
border: 2px ${props.theme.colors.primary} solid;
`}
${props =>
props.secondary &&
`
border: 2px ${props.theme.colors.secondary} solid;
`}
padding: 5px 10px;
background-color: transparent;
border-radius: 5px;
text-align: center;
:focus {
outline: none;
}
`;
const Input = ({
type,
onChange,
placeholder,
primary,
secondary,
className,
value,
name,
ref,
children
}) => {
return (
<Wrap
type={type}
onChange={onChange}
placeholder={placeholder}
primary={primary}
secondary={secondary}
value={value}
name={name}
ref={ref}
className={className}
>
{children}
</Wrap>
);
};
export default Input;
Any help will be useful, Im trying to make a Theme for myself as clean as possible. Thank you.
Adding defaultProps to components works out of the box, even with components created from Styled Components. You need to define the defaultProps on the component. Here's a complete example from a fresh create-react-app application:
import React from 'react';
import './App.css';
import styled from 'styled-components';
const ButtonBlue = styled.button`
background-color: ${props => props.actuallyMakeItGreen ? 'green' : 'blue'};
`;
ButtonBlue.defaultProps = {
actuallyMakeItGreen : true
}
function App() {
return (
<div className="App">
<ButtonBlue>Cool</ButtonBlue>
</div>
);
}
export default App;
Creates a button that looks like this:
import React from 'react';
import './App.css';
import styled from 'styled-components';
const ButtonBlue = styled.button`
background-color: ${props => props.actuallyMakeItGreen ? 'green' : 'blue'};
`;
function App() {
return (
<div className="App">
<ButtonBlue>Cool</ButtonBlue>
</div>
);
}
export default App;
The only thing changing here is inside the defaultProps property, where we default actuallyMakeItGreen to true rather than false.
First ensure you've passed your props into the styled component
<Button props={this.props} />
Then in your styled component you should be to format like so
const Button = styled.button`
${({ props }) => `
color: ${props.color};
`}
`
By default in react we receive react-toastify like in attached image below.
But now project demands all messages in one block or div, with one cross button and notification count on top with one timer and with a scrollbar to see all messages.
I have tried with redux but now need to remove redux. Please see if anyone can help in this by using only react. Or if react-toastify giving this functionality.
import React, { useContext, useState, useReducer } from 'react';
import { ToastContext } from './App';
import { closeToast, showToast } from '../store/toast/action';
import { connect } from 'react-redux';
const ToastContainer = styled.div`
width: 350px;
position: absolute;
top: 100px;
right: 16px;
background: #c12335;
z-index: 9999;
color: white;
padding: 12px;
display: ${props => (props.hide ? 'none' : '')};
`;
const ViewMore = styled.div`
text-align: right;
margin-top: 4px;
text-decoration: underline;
cursor: pointer;
`;
const CloseButton = styled.div`
color: #fff;
font-weight: bold;
font-size: 14px;
background: transparent;
outline: none;
border: none;
padding: 0;
cursor: pointer;
text-align: right;
`;
const ToastList = styled.div`
margin-top: 8px;
max-height: 160px;
overflow: auto;
`;
const CustomToast = props => {
const { toastList, closeToast } = props;
const [showAll, setShowAll] = useState(false);
const toastCount = toastList.length;
const list = showAll ? toastList : toastList.slice(0, 5);
return (
<ToastContainer hide={toastCount == 0}>
<div>
<CloseButton
onClick={() => {
setShowAll(false);
closeToast();
}}
>
x
</CloseButton>
{toastCount} New Notifications
</div>
<PerfectScrollbar>
<ToastList>
{list.map((toastContent, index) => (
<div key={'toast-' + index}>
<span class="fa fa-exclamation-triangle"></span> {toastContent}
</div>
))}
</ToastList>
</PerfectScrollbar>
{toastList.length > 5 && !showAll && (
<ViewMore onClick={() => setShowAll(true)}>View More....</ViewMore>
)}
</ToastContainer>
);
};
const mapStateToProps = state => {
return {
toastList: state.toast.toastList,
};
};
const mapDispatchToProps = {
showToast,
closeToast,
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(CustomToast);
I have this code:
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import styled from "styled-components";
import ArrowTemplate from "./ArrowTemplate";
const AccordionBtn = styled.button`
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
display: flex;
align-items: center;
border: none;
outline: none;
transition: background-color 0.6s ease;
:hover,
:focus,
:active {
background-color: #ccc;
}
`;
const AccordionTitle = styled.p`
font-family: "Open Sans", sans-serif;
font-weight: 600;
font-size: 14px;
`;
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
const AccordionText = styled.div`
font-family: "Open Sans", sans-serif;
font-weight: 400;
font-size: 14px;
padding: 18px;
`;
const AccordionSection = styled.div`
display: flex;
flex-direction: column;
`;
Accordion.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.node.isRequired,
id: PropTypes.string.isRequired,
};
function Accordion(props) {
const [isAccordionExpanded, setIsAccordionExpanded] = useState(false);
const toggleAccordion = () => {
setIsAccordionExpanded(!isAccordionExpanded);
};
return (
<AccordionSection>
<AccordionBtn onClick={toggleAccordion}>
<AccordionTitle>
{props.title}
</AccordionTitle>
<ArrowTemplate
color={'black'}
direction={isAccordionExpanded === true ? 'up' : 'down'}
onClick={toggleAccordion}
/>
</AccordionBtn>
<AccordionContent
style={{height: isAccordionExpanded === true ? "100px" : "0"}}
>
<AccordionText>
{props.content}
</AccordionText>
</AccordionContent>
</AccordionSection>
);
}
export default Accordion;
What this code does, is extends the accordeon on click. Preety simple. But now, I want to move this height:
<AccordionContent
style={{height: isAccordionExpanded === true ? "100px" : "0"}}
>
here:
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
The problem is I need to use state, and if I declare it in the function, it will get re-rendered, and not run the animation. How can I pass the state to the styled-component?
You can just pass the isAccordionExpanded directly to the styled component. Change your style from this:
const AccordionContent = styled.div`
background-color: red;
overflow: hidden;
transition: 0.6s;
`;
to include this
height: ${({ isAccordionExpanded }) => (isAccordionExpanded ? "100px" : "0"};
Another way to do animation in React is using React Transition Group, Quite good
How can I make a CSS transform work with a react component? The user presses a div (side-nav-4a) below, and it renders a component in another sibling component. The CSS transform causes nothing to render. Removing the transform in CSS will render the component.
CSS
side-nav-4a {
grid-column: 1 / span 1;
grid-row: 17 / span 4;
background: #EFF0F0;
font-family: 'Lato', sans-serif;
font-size: medium;
transition: background 0.2s ease;
color: #757575;
text-align: center;
min-width: 11.4rem;
&:hover, &:focus {
background: #DADBDB;
cursor: pointer;
}
//&:active {
// transform: scale(.97);
// z-index: -1;
//}
}
.openings-feesA-key {
text-align: center;
vertical-align: middle;
line-height: 10rem;
}
Clicked div (React component):
import React from "react";
export default class SideBar4a extends React.Component {
render() {
const date = new Date().getFullYear();
return (
<div onClick={this.props.setOpeningsAndFeesA} className='openings-feesA-key'>
<h3>FEES {date}</h3>
</div>
);
}
}
React component that renders:
import React from 'react';
export default () => {
return (
<div className='intermezzi-content'>
<p>some intermezzi content</p>
</div>
)
}