React Js Button Toggle/ Styled Components - reactjs

my style
import React from 'react';
import styled from 'styled-components';
export const DivMenuButton = styled.div`
border: 0px;
backgroundColor: #000;
height: 400px;
width: 200px;
`;
my return:
import { DivMenuButton } from './styles';
export default function Menu() {
const [open, setOpen] = useState(0);
const handleClick = e => {
e.preventDefault();
setOpen(!open);
};
return (
<DivMenuButton>
<Button
style={{ margin:0, padding: 0, height: "30px", width: "100%", borderRadius:'0px' }}
onClick={handleClick}
>
Button
</Button>
</DivMenuButton>
);
}
I would also like to know how I could do the following:
I have a state open
my div will start with 400 px
clicking the button will get it 30px
but I don't know how I can do this with styled components

Use styled-components props
Try this:
export const DivMenuButton = styled.div`
border: 0px;
background-color: #000; // was wrong syntax
height: 400px;
width: ${props => props.width}
`;
export default function Menu() {
const [open, setOpen] = useState(false);
const handleClick = e => {
// e.preventDefault(); no need
setOpen(!open);
};
return (
<DivMenuButton width={open ? '30px' : '400px'}>
<button
style={{ margin:0, padding: 0, height: "30px", width: "100%", borderRadius:'0px' }}
onClick={handleClick}
>
Button
</button>
</DivMenuButton>
);
}

Related

handling events on custom components

I have these files:
button.module.css
.base {
display: flex;
font-size: 16px;
font-family: "Helvetica Neue";
align-items: center;
justify-content: center;
padding: 6px 12px 6px 12px;
width: 50px;
border-radius: 6px;
}
.normal {
composes: base;
color:black;
background-color: #aeeeb7;
border: 1px solid #42924d;;
}
.danger {
composes: base;
color:black;
background-color: rgb(253, 128, 128);
border: 1px solid rgb(196, 108, 108);
}
button.js
import classes from './button.module.css';
const ButtonTypes = {
'normal': classes.normal,
'danger': classes.danger
};
const Button = (props) => {
const className = ButtonTypes[props.type] ? ButtonTypes[props.type] : classes.normal;
return <div role="button" className={className} ...props>{text}</div>;
}
export default Button;
app.js
import Button from './button';
const App = () => {
const handleClick = () => {
console.log('app');
}
const handleMouseOver = () => {
console.log('mouse-over');
}
...
return (
<div>
<Button type="normal" onClick=(handleClick) onMouseOver={handleMouseOver} ...>
</div>
);
}
export default Button;
I am changing the class of the button depending on the "type" property. (It could be other components not just a button)
How should I handle events on the custom component? Is the way I am passing them right now to that div inside fine or I should do it differently?
I want to create some custom components and I am trying to verify I am doing it the right way.
One suggestion here. Instead of spreading all props in button. You can destructure out the required props and then spread the rest props.
import classes from "./button.module.css";
const ButtonTypes = {
normal: classes.normal,
danger: classes.danger
};
const Button = ({ type, ...rest }) => {
const className = ButtonTypes[type] ? ButtonTypes[type] : classes.normal;
return (
<div role="button" className={className} {...rest}>
{text}
</div>
);
};
export default Button;

Passing a direction prop into styled-components keyframe component?

I'm trying to implement a reusable text animation component where a direction prop can be passed in. I probably close but doesn't seem to be working. Also open to better ways of implementing it a better way.
import React from "react"
import styled from "styled-components"
import { GlobalStyles } from "./global"
import TextAnimations from "./Components/TextAnimations"
const Container = styled.div`
display: flex;
justify-content: space-between;
flex-wrap: wrap;
`
const NavBar = styled.nav`
background: #3a3a55;
padding: 0.25rem;
width: 100%;
height: 10vh;
`
const Main = styled.main`
background: #3a3a55;
color: white;
padding: 0.25rem;
flex: 10 1 auto;
height: 100vh;
`
const Break = styled.div`
flex-basis: 100%;
width: 0;
`
function App() {
return (
<>
<GlobalStyles />
<Container>
<NavBar>NavBar</NavBar>
<Break />
<Main>
<TextAnimations text='Sample text from the left' direction='left' />
<TextAnimations text='Sample text from the right' direction='right' />
</Main>
</Container>
</>
)
}
export default App
and then the animation component:
import { motion } from "framer-motion"
import styled, { keyframes } from "styled-components"
type TextAnimationProps = {
text: string
direction: string
}
const Left = keyframes`
0% { left: -3.125em; }
100% { left: 3em;}
`
const Right = keyframes`
0% { Right: -3.125em; }
100% { Right: 3em;}
`
const HomeHeader = styled.div`
h1 {
font-weight: lighter;
}
position: relative;
top: 0;
animation: ${(props) => (props.defaultValue === "left" ? Left : Right)} // is this right?
animation-duration: 3s;
animation-fill-mode: forwards;
`
const TextAnimations = ({ text, direction }: TextAnimationProps) => {
return (
<HomeHeader defaultValue={direction}>
<h1>{text}</h1>
</HomeHeader>
)
}
export default TextAnimations
I'd also like to make it more flexible so that I can add 'top', 'bottom', etc.
Is this the best way to handle text animations?
You can create a separate function to set the animation. The function will receive the props of the styled component from which the function will access the direction prop.
const setHomeHeaderAnimation = ({ direction = "left" }) => {
const animation = keyframes`
0% {
${direction}: -3.125em;
}
100% {
${direction}: 3em;
}
`;
return css`
position: relative;
animation: ${animation};
animation-duration: 3s;
animation-fill-mode: forwards;
`;
};
const HomeHeader = styled.div`
${setHomeHeaderAnimation}
h1 {
font-weight: lighter;
}
`;
const App = () => (
<div>
<HomeHeader>
<div>Some text</div>
</HomeHeader>
<HomeHeader direction="right">
<div>Some text</div>
</HomeHeader>
<HomeHeader direction="top">
<div>Some text</div>
</HomeHeader>
<HomeHeader direction="bottom">
<div>Some text</div>
</HomeHeader>
</div>
);

Getting this error Uncaught Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop

I am trying to move my div(bar) as my states gets updated using props. but this error occurs.
import React, { useState } from "react";
import { data as TableData } from "../Table/alan_requested_format";
import "./styles.css";
export const Playhead = (props) => {
const [incVal, setIncVal] = useState(0);
let width = 15;
let incrementedValue = (width / TableData.duration) * props.currentTime;
console.log("incrementedValue", incrementedValue);
setIncVal(incrementedValue);
const style = {
backgroundColor: "#ffffff",
border: "6px solid #ffffff",
height: "75px",
width: `${width}px`,
borderRadius: "5px",
position: "absolute",
left: { incVal },
padding: "0px 4px 0px 4px",
};
return (
<>
<div class="vl" style={style}></div>
</>
);
};
When I try to run this code the code bursts. The whole web page got crashed.
You wrote your setter setIncVal inside the render function (function component's body), therefore you actually trigger state change on every render which causes the infinite loop.
I believe you wanted to trigger this change only on currentTime change, therefore use the proper API with useEffect:
let width = 15;
export const Playhead = (props) => {
const [incVal, setIncVal] = useState(0);
useEffect(() => {
let incrementedValue = (width / TableData.duration) * props.currentTime;
console.log("incrementedValue", incrementedValue);
setIncVal(incrementedValue);
}, [props.currentTime]);
const style = {
backgroundColor: "#ffffff",
border: "6px solid #ffffff",
height: "75px",
width: `${width}px`,
borderRadius: "5px",
position: "absolute",
left: { incVal },
padding: "0px 4px 0px 4px",
};
return (
<>
<div class="vl" style={style}></div>
</>
);
};
This works fine for me.
import React from "react";
import "./styles.css";
export const Playhead = (props) => {
let incrementedValue = props.TableData.duration / window.innerWidth;
const style = {
backgroundColor: "#ffffff",
border: "1px solid #ffffff",
height: "95px",
borderWidth: '2px',
width: "2px",
borderRadius: "5px",
position: "absolute",
left: incrementedValue * props.currentTime,
padding: "0px 4px 0px 4px",
};
return (
<>
<div class="vl" style={style}></div>
</>
);
};

Using styled-components

Can someone tell me why my styled components aren't working. I was following an example where the person put the css right in their react app using back ticks but my is not working like thiers.
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
position: absolute;
left: ${(props) => (props.position === "left" ? "20px" : "380px")}
top: 20px
background: ${(props) => (props.lampOn ? "orange" : "lightgrey")};
width: 100px;
height: 100px;
border-radius: 50%;
`;
const Lamp = ({ lampOn, position }) => {
return (
<Wrapper lampOn={lampOn} position={position}>
<div>Test light oo </div>
</Wrapper>
);
};
export default Lamp;
Missing semi colons:
Refer to https://stackblitz.com/edit/react-thagj7
import React from 'react';
import styled from 'styled-components';
export default function App() {
return (
<div>
<h1>Hello StackBlitz!</h1>
<Lamp lampOn position="left" />
</div>
);
}
const Wrapper = styled.div`
position: absolute;
left: ${props => (props.position === 'left' ? '20px' : '380px')};
top: 200px;
background: ${props => (props.lampOn ? 'orange' : 'lightgrey')};
width: 100px;
height: 100px;
border-radius: 50%;
`;
const Lamp = ({ lampOn, position }) => {
return (
<Wrapper lampOn={lampOn} position={position}>
<div>Test light oo </div>
</Wrapper>
);
};

React Context API returns undefined

I'm quite new to React, and i'm trying to make a ToDoList. I have a Modal with a submit button that when pressed should add a ToDoItem. But since i didn't want to prop drill my way through this i wanted to use the Context API. The Context API confuses me quite a bit, maybe i'm just a moron, but i have a hard time understanding why i have to make a hook and pass that as the value in the provider. I thought that in the ToDoContext that i already defined the default value as a empty array, so i just did it again.
In the console at line 62, which is my initial render it says that it's undefined, after the pressing the Add ToDo I get the same message.
App.jsx
import React, { useState } from "react";
import { render } from "react-dom";
import { ThemeProvider } from "emotion-theming";
import { defaultTheme } from "./theme";
import { Global, css } from "#emotion/core";
import Header from "./components/Header";
import ToDoList from "./components/ToDoList";
import AddBtn from "./components/AddBtn";
import ToDoContext from "./ToDoContext";
const App = () => {
const [toDoItems] = useState([]);
return (
<>
{/*Global styling*/}
<Global
styles={css`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style: none;
text-decoration: none;
}
`}
/>
{/*App render start from here*/}
<ThemeProvider theme={defaultTheme}>
<ToDoContext.Provider value={toDoItems}>
<Header />
<main>
<ToDoList />
<AddBtn />
</main>
</ToDoContext.Provider>
</ThemeProvider>
</>
);
};
render(<App />, document.getElementById("root"));
ToDoContext.jsx
import { createContext } from "react";
const ToDoContext = createContext([[], () => {}]);
export default ToDoContext;
AddBtn.jsx
import React, { useState, useContext } from "react";
import { css } from "emotion";
import Modal from "../Modal";
import ToDoContext from "../ToDoContext";
const BtnStyle = css`
position: fixed;
bottom: 0;
right: 0;
cursor: pointer;
display: block;
font-size: 7rem;
`;
const ModalDiv = css`
position: fixed;
left: 50%;
background-color: #e6e6e6;
width: 60%;
padding: 20px 20px 100px 20px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 400px;
height: 50%;
transform: translate(-50%, -50%);
border-radius: 20px;
top: 50%;
`;
const textareaStyle = css`
resize: none;
width: 100%;
height: 200px;
font-size: 1.25rem;
padding: 5px 10px;
`;
const timeStyle = css`
font-size: 3rem;
display: block;
`;
const modalSubmit = css`
width: 100%;
font-size: 3rem;
cursor: pointer;
margin-top: auto;
`;
const Label = css`
font-size: 2rem;
text-align: center;
display: inline-block;
margin-bottom: 50px;
`;
const AddBtn = () => {
const [showModal, setShowModal] = useState(true);
const [time, setTime] = useState("01:00");
const [toDoItems, setToDoItems] = useContext(ToDoContext);
console.log(toDoItems);
return (
<>
<div className={BtnStyle} onClick={() => setShowModal(!showModal)}>
<ion-icon name="add-circle-outline"></ion-icon>
</div>
{showModal ? (
<Modal>
<div className={ModalDiv}>
<div>
<label className={Label} htmlFor="time">
Time
<input
className={timeStyle}
type="time"
name="time"
value={time}
onChange={(e) => setTime(e.target.value)}
/>
</label>
</div>
<label className={Label} htmlFor="desc">
Description
<textarea
className={textareaStyle}
name="desc"
placeholder={`Notify yourself this message in ${time}`}
></textarea>
</label>
<button
className={modalSubmit}
onClick={() => {
setToDoItems(
toDoItems.push({
time,
})
);
}}
>
Add ToDo
</button>
</div>
</Modal>
) : null}
</>
);
};
export default AddBtn;
There are few issues in your code to fix:
useState returns a value and a setter. With this line of code, const [toDoItems] = useState([]);, you are just passing an empty array to your context.
So do this:
const toDoItems = useState([]);
In your ToDoContext.js, just pass an empty array as argument (initial value)
const ToDoContext = createContext([]);
Working copy of your code is here. (see console logs)
Also, I noticed that you are pushing the todo in setTodoItems in AddBtn.js.
Don't do this:
onClick={() => {
setToDoItems(
toDoItems.push({
time
})
);
}}
Do this:
onClick={() => {
setToDoItems(
toDoItems.concat([
{
time
}
])
);
}}

Resources