eact Hook useEffect has a missing dependency: ' xyz '. Either include it or remove the dependency array - reactjs

I'm trying to add infinite scroll to my page but it's only showing me the mongo data. and while running react it's showing me a warning that says:-
"src\components\Article.jsx
React Hook useEffect has a missing dependency: 'news'. Either include it or remove the dependency array. You can also do a functional update 'setNews(n => ...)' if you only need 'news' in the 'setNews' call react-hooks/exhaustive-deps"
Article.jsx
import { useEffect , useState} from "react";
import { getNews } from "../service/api";
import InfiniteScroll from 'react-infinite-scroll-component';
import Articles from "./Articles";
const Article = () => {
const [news, setNews] = useState([]);
const [page, setPage] = useState(0);
useEffect(() => {
const dailyNews = async () => {
const response = await getNews(page);
console.log(new Set([...news, ...response.data]));
setNews([...new Set([...news, ...response.data])]);
}
dailyNews();
}, [page])
useEffect(() => {
console.log(news);
}, [news])
return(
<InfiniteScroll
dataLength={news.length *5 }
pullDownToRefreshThreshold={50}
next ={ () => setPage(page => page + 1)}
hasMore={true}
loader={<h4>Loading...</h4>}>
{
news.map(article =>(
<Articles article={article} />
))
}
</InfiniteScroll >
)
}
export default Article;
and Articles.jsx
import { Card, CardContent, Typography, styled, Grid } from "#mui/material";
const Component = styled(Card)`
box-shadow: 0 2px 5px 0 rgb(0 0 0 / 16%), 0 2px 10px 0 rgb(0 0 0 / 12%);
margin-bottom: 20px;
`;
const Container = styled(CardContent) `
display: flex;
padding: 8px;
padding-bottom: 4px !important;
`;
const Image = styled('img')({
height: 268,
width: '88%',
borderRadius: 5,
objectFit: 'cover'
});
const RightContainer = styled(Grid)(({ theme }) => ({
margin: '5px 0px 0 -25px',
display: 'flex',
flexDirection: 'column',
[theme.breakpoints.between('sm', 'lg')]: {
padding: '0 5px'
},
[theme.breakpoints.down('sm')]: {
margin: '5px 0'
}
}));
const Title = styled(Typography)`
font-weight: 300;
color: #44444d;
font-size: 22px;
line-height: 27px;
`;
const Author = styled(Typography)`
color: #808290;
font-size: 12px;
line-height: 22px;
`;
const Description = styled(Typography)`
line-height: 22px;
color: #44444d;
margin-top: 5px;
font-family: 'Roboto',sans-serif;
font-weight: 300;
`;
const Short = styled('b')({
color: '#44444d',
fontFamily: "'Convergence', sans-serif"
})
const Publisher = styled(Typography)`
font-size: 12px;
margin-top: auto;
margin-bottom: 10px;
'& > *': {
textDecoration: 'none',
color: '#000',
fontWeight: 900
}
`;
const Articles = ({ article }) => {
return (
<Component>
<Container>
<Grid container>
<Grid lg={5} md={5} sm={5} xs={12} item>
<Image src={article.url} />
</Grid>
<RightContainer lg={7} md={7} sm={7} xs={12} item>
<Title>{article.title}</Title>
<Author>
<Short>short</Short> by {article.author} / {new Date(article.timestamp).toDateString()}
</Author>
<Description>{article.description}</Description>
<Publisher>
read more at {article.publisher}
</Publisher>
</RightContainer>
</Grid>
</Container>
</Component>
)
}
export default Articles;
Try to write like this
useEffect(() => {
const dailyNews = async () => {
const response = await getNews(page);
console.log(new Set([...news, ...response.data]));
setNews([...new Set([...news, ...response.data])]);
}
dailyNews();
//eslint-disable-next-line
}, [page])
Please please help me in adding infinite scroll to my page
I'm using "react-infinite-scroll-component" npm package

Use following format for all useEffect hook
useEffect(()=> {
...
//eslint-disable-next-line
},[])

You can put
// eslint-disable-next-line react-hooks/exhaustive-deps
before the violating line if there is a good reason.
Read react team member comment
Write useEffect like this
useEffect(() => {
const dailyNews = async () => {
const response = await getNews(page);
console.log(new Set([...news, ...response.data]));
setNews([...new Set([...news, ...response.data])]);
}
dailyNews();
// eslint-disable-next-line
}, [page])
or you can add missing dependencies there like this
useEffect(() => {
const dailyNews = async () => {
const response = await getNews(page);
console.log(new Set([...news, ...response.data]));
setNews([...new Set([...news, ...response.data])]);
}
dailyNews();
}, [page, news, setNews])

Related

Styled-component stopped working on one page. React

I'm using styled-components on my app. Styling works on home page, and all other pages except one List page. When I open this page all components (a mean tags which i created by styled-components) rendering on page, but styling not working. After I open this page, when i go to another pages which worked correctly before, styles on this pages stop working also. It's come back after page reload, but stop working again after opening List page.
here is List page code:
import React, { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import styled from 'styled-components'
import {
Footer,
ListFilters,
ListProduct,
Navbar,
SearchFilterItem,
} from '../components'
const ListContainer = styled.div`
width: 100%;
`
const ListWrapper = styled.div`
min-height: 100vh;
width: 93vw;
margin: 0 auto;
padding: 30px 0px;
display: flex;
align-items: center;
opacity: ${(props) => props.o};
`
const ProductsContainer = styled.div`
flex: 5;
align-self: flex-start;
`
const ListHeader = styled.div`
display: flex;
flex-direction: column;
padding: 0px 0px 15px 20px;
`
const ListHeaderTop = styled.div`
display: flex;
justify-content: space-between;
`
const ListHeaderBottom = styled.div`
display: flex;
justify-content: flex-start;
margin-top: 10px;
padding-bottom: 10px;
`
const ListHeadeTitle = styled.h1`
font-size: 17px;
font-weight: 500;
`
const FilterBy = styled.select`
padding: 2px 5px;
width: 180px;
border: 1px solid #ccc;
color: #666;
&:focus {
outline: none;
}
`
const FilterByOption = styled.option``
const Products = styled.div`
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
padding-left: 20px;
`
const List = () => {
const location = useLocation()
const [loading, setLoading] = useState(false)
const [error, setError] = useState(false)
const [products, setProducts] = useState([])
const [filters, setFilters] = useState({
categoryFilter: [],
brandFilter: [],
sizeFilter: [],
genderFilter: [],
})
const [cat, setCat] = useState(null)
useEffect(() => {
if (location?.state?.item) {
setCat({ ...cat, category: location?.state?.item })
} else if (location?.state?.brand) {
setCat({ ...cat, brand: location?.state?.brand })
} else {
setCat(null)
}
}, [])
// console.log(location.state)
const qs =
cat &&
Object.keys(cat)
.map(
(key) =>
`${encodeURIComponent(key)}=${encodeURIComponent(cat[key])}`
)
.join('&')
useEffect(() => {
let isMounted = true
const source = axios.CancelToken.source()
const getProducts = async () => {
setLoading(true)
try {
const res = await axios.get(
cat
? `http://localhost:5000/api/products?${qs}`
: `http://localhost:5000/api/products`,
{
cancelToken: source.token,
}
)
if (isMounted) {
setProducts(res.data)
setError(null)
const allCats = [
...new Set(
res.data.map((item) => item.category).flat()
),
]
const allBrands = [
...new Set(res.data.map((item) => item.brand)),
]
const allGenders = [
...new Set(res.data.map((item) => item.gender)),
]
const allSizes = [
...new Set(res.data.map((item) => item.size).flat()),
]
setFilters({
categoryFilter: allCats,
brandFilter: allBrands,
sizeFilter: allSizes,
genderFilter: allGenders,
})
}
} catch (error) {
if (isMounted) {
setError(error.message)
setProducts(null)
}
} finally {
isMounted && setLoading(false)
}
}
getProducts()
const cleanUp = () => {
isMounted = false
source.cancel()
}
return cleanUp
}, [cat, qs])
const onToggleFilter = (categ, id) => {
if (cat && Object.entries(cat).flat().includes(id)) {
setCat(
Object.fromEntries(
Object.entries(cat).filter(([key, value]) => value !== id)
)
)
} else {
if (categ === 'gender') {
setCat({ ...cat, gender: id })
} else if (categ === 'size') {
setCat({ ...cat, size: id })
} else if (categ === 'brand') {
setCat({ ...cat, brand: id })
} else {
setCat({ ...cat, category: id })
}
}
}
const onDeleteFilter = (id) => {
// console.log(id)
setCat(
Object.fromEntries(
Object.entries(cat).filter(([key, value]) => value !== id)
)
)
}
console.log(cat)
return (
<>
<Navbar />
<ListContainer>
<ListWrapper o={loading ? 0.35 : 1}>
{/* {loading ? (
<Spinner />
) : (
<> */}
<ListFilters
cat={cat}
setCat={setCat}
filters={filters}
onToggleFilter={onToggleFilter}
/>
<ProductsContainer>
<ListHeader>
<ListHeaderTop>
<ListHeadeTitle>
{products.length} result
{products.length > 1 ? 's' : null}
{' are listed for your search'}
</ListHeadeTitle>
<FilterBy>
<FilterByOption>Sort By:</FilterByOption>
<FilterByOption value="new">
Newest Arrivals
</FilterByOption>
<FilterByOption value="low">
Price: Low
</FilterByOption>
<FilterByOption value="high">
Price: High
</FilterByOption>
</FilterBy>
</ListHeaderTop>
<ListHeaderBottom>
{cat &&
Object.keys(cat).map((key) => (
<SearchFilterItem
onDeleteFilter={onDeleteFilter}
key={key}
info={cat[key]}
/>
))}
</ListHeaderBottom>
</ListHeader>
<Products>
{products.map((item) => (
<ListProduct key={item._id} {...item} />
))}
</Products>
</ProductsContainer>
{/* </>
)} */}
</ListWrapper>
</ListContainer>
<Footer />
</>
)
}
export default List

Values getting undefined when consuming data using useContext in the child component

It is my first time working with useContext hook and I was trying to consume values with useContext in Navbar.js which I've provided in the parent component, but i'm receiving undefined only. In the Navbar.js when I console log user it showsundefined.
I tried to pass the data as props and it works fine. It only happens when I try with Context API. I think I messed up my context api implementation.
Created a context at AuthContext.js
import {createContext} from 'react'
export const AuthContext = createContext({})
Set values in App.js and wrapped using Provider.
import Login from "./pages/Login";
import Register from "./pages/Register";
import Cart from "./components/Cart/Cart";
import Slider from "./components/Slider";
import Categories from "./components/Categories";
import PopularProducts from "./components/PopularProducts";
import Navbar from "./components/Navbar";
import Checkout from "./components/Cart/Checkout";
import Products from "./components/Products";
import Footer from "./components/Footer";
import {
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
createUserWithEmailAndPassword,
} from "firebase/auth";
import { auth } from "./lib/firebase-config";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { useState, useEffect } from "react";
import { commerce } from "./lib/commerce";
import { AuthContext } from "./context/AuthContext";
const App = () => {
const [products, setProducts] = useState([]);
const [cart, setCart] = useState({});
const [order, setOrder] = useState({});
const [errorMessage, setErrorMessage] = useState("");
const [loginEmail, setLoginEmail] = useState("");
const [loginPassword, setLoginPassword] = useState("");
const [user, setUser] = useState({});
const [registerEmail, setRegisterEmail] = useState("");
const [registerPassword, setRegisterPassword] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(
localStorage.getItem("isLoggedIn")
);
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
const loginHandler = async (event) => {
event.preventDefault();
try {
const user = await signInWithEmailAndPassword(
auth,
loginEmail,
loginPassword
);
setIsLoggedIn(true);
localStorage.setItem("isLoggedIn", true);
console.log(auth.currentUser.email);
} catch (error) {
console.log(error.message);
}
};
const registerHandler = async () => {
try {
const user = await createUserWithEmailAndPassword(
auth,
registerEmail,
registerPassword
);
setIsLoggedIn(true);
localStorage.setItem("isLoggedIn", true);
console.log({ registerEmail });
console.log(user);
} catch (error) {
console.log(error.message);
}
};
const logoutHandler = async () => {
await signOut(auth);
setIsLoggedIn(false);
localStorage.clear();
};
const fetchProducts = async () => {
const { data } = await commerce.products.list();
setProducts(data);
};
const fetchCart = async () => {
setCart(await commerce.cart.retrieve());
};
const handleAddToCart = async (productId, quantity) => {
const item = await commerce.cart.add(productId, quantity);
setCart(item.cart);
};
const handleUpdateCartQty = async (lineItemId, quantity) => {
const response = await commerce.cart.update(lineItemId, { quantity });
setCart(response.cart);
};
const handleRemoveFromCart = async (lineItemId) => {
const response = await commerce.cart.remove(lineItemId);
setCart(response.cart);
};
const handleEmptyCart = async () => {
const response = await commerce.cart.empty();
setCart(response.cart);
};
const refreshCart = async () => {
const newCart = await commerce.cart.refresh();
setCart(newCart);
};
const handleCaptureCheckout = async (checkoutTokenId, newOrder) => {
try {
const incomingOrder = await commerce.checkout.capture(
checkoutTokenId,
newOrder
);
setOrder(incomingOrder);
refreshCart();
} catch (error) {
setErrorMessage(error.data.error.message);
console.log(error.data);
}
};
useEffect(() => {
fetchProducts();
fetchCart();
}, []);
return (
<>
<AuthContext.Provider
value={
(user,
logoutHandler,
isLoggedIn) }>
<Navbar/>
</AuthContext.Provider>
</>
);
};
export default App;
Trying to consume data using useContext in Navbar.js
import { Badge } from "#material-ui/core";
import {
Search,
ShoppingCartOutlined,
TranslateOutlined,
} from "#material-ui/icons";
import { useContext } from "react";
import styled from "styled-components";
import { mobile } from "../responsive";
import { Link } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";
const Parent = styled.div``;
const Container = styled.div`
height: 70px;
background-color: #b6e7f0;
${mobile({ height: "70px" })}
width: 98.75vw;
position: fixed;
top: 0;
z-index: 4;
`;
const Logo = styled.h4`
font-size: 30px;
color: black;
margin-left: 20px;
${mobile({ fontSize: "30px" })}
`;
const Wrapper = styled.div`
padding: 10px 0px;
display: flex;
justify-content: space-between;
max-width: 98.75vw;
${mobile({ padding: "10px 0px" })}
`;
const Left = styled.div`
display: flex;
align-items: center;
text-decoration: none;
width: 20%;
`;
const Center = styled.div``;
const Right = styled.div`
display: flex;
align-items: center;
margin-right: 20px;
justify-content: flex-end;
text-decoration: none;
${mobile({ flex: 2, justifyContent: "flexEnd" })}
`;
const Language = styled.span`
font-size: 14px;
cursor: pointer;
${mobile({ display: "none" })}
`;
const SearchContainer = styled.div`
flex: 1;
display: flex;
align-items: center;
margin-left: 0px;
margin-top: 10px;
padding: 5 px;
border-radius: 5px;
cursor: pointer;
${mobile({ display: "none" })}
&:focus {
}
`;
const Input = styled.input`
border: none;
&:focus {
outline: none;
}
width: 90%;
height: 100%;
border-radius: 5px;
font-size: 15px;
padding: 5px;
margin-right: 20px;
${mobile({ height: "50%" })}
`;
const MenuItem = styled.div`
margin-left: 25px;
margin-top: 3px;
font-size: 18px;
font-weight: 600;
color: #0a6596e6;
cursor: pointer;
justify-content: space-between;
${mobile({ fontSize: "13px", marginLeft: "10px" })}
`;
const Navbar = () => {
const { user, logoutHandler, isLoggedIn } = useContext(AuthContext);
console.log(user)
return (
<Parent>
<Container>
<Wrapper>
<Left>
<Link to={"/"}>
<Logo>audiobae</Logo>
</Link>
</Left>
<SearchContainer style={{ color: "gray", fontSize: 14 }}>
<Input placeholder="Search for products, brands and more" />
<Search style={{ fontSize: "30px" }} />
</SearchContainer>
<Right>
<Language style={{ marginLeft: "5px", marginTop: "3px" }}>
<TranslateOutlined />
</Language>
{console.log(isLoggedIn)}
{isLoggedIn ? (
<>
<MenuItem>{user?.email}</MenuItem>
<MenuItem onClick={logoutHandler}>Logout</MenuItem>
</>
) : (
<>
<Link to={"/register"} style={{ textDecoration: "none" }}>
<MenuItem>Register </MenuItem>
</Link>
<Link to={"/login"} style={{ textDecoration: "none" }}>
<MenuItem>Login</MenuItem>
</Link>
</>
)}
<MenuItem>
<Link to={"/cart"}>
<Badge badgeContent={cart?.total_items} color="primary">
{console.log(cart)}
<MenuItem>
<ShoppingCartOutlined />
</MenuItem>
</Badge>
</Link>
</MenuItem>
</Right>
</Wrapper>
</Container>
</Parent>
);
};
export default Navbar;
try it
<AuthContext.Provider value={{user,logoutHandler,isLoggedIn}}>
<Navbar/>
</AuthContext.Provider>

Adding A button before the previousLabel Props and after the nextLabel Props in React-Paginate Not Working

Please I needed to add a button before the previousLabel property details and after the nextLabel prop details in React-Paginate but what I have below is not working. I have also tried to read the documentation and check all the prop details but still couldn't achieve the result below.
I want a first button to come before one button and a last button to come after the next button.
what the code below looks like above.
what I want to achieve below
```
import React, { useState, useEffect } from 'react'
import axios from 'axios'
import ReactPaginate from 'react-paginate';
import './styles.css'
export default function App() {
const [postsPerPage] = useState(5);
const [offset, setOffset] = useState(1);
const [posts, setAllPosts] = useState([]);
const [pageCount, setPageCount] = useState(0)
const getPostData = (data) => {
return (
data.map(post => <div className="container" key={post.id}>
<p>User ID: {post.id}</p>
<p>Title: {post.title}</p>
</div>)
)
}
const getAllPosts = async () => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/posts`)
const data = res.data;
const slice = data.slice(offset - 1 , offset - 1 + postsPerPage)
// For displaying Data
const postData = getPostData(slice)
// Using Hooks to set value
setAllPosts(postData)
setPageCount(Math.ceil(data.length / postsPerPage))
}
const handlePageClick = (event) => {
const selectedPage = event.selected;
setOffset(selectedPage + 1)
};
useEffect(() => {
getAllPosts()
}, [offset])
return (
<div className="main-app">
{/* Display all the posts */}
{posts}
{/* Using React Paginate */}
<ReactPaginate
previousLabel={"previous"}
nextLabel={"next"}
breakLabel={"..."}
breakClassName={"break-me"}
pageCount={pageCount}
onPageChange={handlePageClick}
containerClassName={"pagination"}
subContainerClassName={"pages pagination"}
activeClassName={"active"} />
</div>
);
}
```
Below is the css
.main-app {
margin: 2% 10%;
border: 1px ridge #464141;
padding: 5%;
font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
font-size: 20px;
color: #464141;
}
.container {
border-bottom: #fff 2px ridge;
}
.pagination {
margin-top: 45px;
margin-left: -40px;
display: flex;
list-style: none;
outline: none;
}
.pagination>.active>a {
background-color: #47ccde;
color: #fff;
}
.pagination>li>a {
border: 1px solid #47ccde;
padding: 5px 10px;
outline: none;
cursor: pointer;
}
.pagination>li>a, .pagination>li>span {
color: #47ccde;
background-color: azure;
}

React Storybook Checkbox change handler not working

I am creating a checkbox component using React, Typescript, Storybook and Styled-Components, after following this tutorial: building-a-checkbox-component-with-react-and-styled-components. I have had to adjust the code as I am using a FunctionComponent, but I am facing an issue with the change handler. I cannot check or uncheck the Checkbox which seems to be readonly, and I'm not sure where I am going wrong. Here is my code:
Checkbox.tsx
import React from 'react';
import styled from 'styled-components';
import { FunctionComponent } from 'react';
type CheckboxProps = {
checked?: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
const CheckboxContainer = styled.div`
display: inline-block;
vertical-align: middle;
`;
const Icon = styled.svg`
fill: none;
stroke: white;
stroke-width: 2px;
`;
const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
border: 0;
clip: rect(0 0 0 0);
clippath: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`;
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: ${(props: CheckboxProps) => (props.checked ? 'green' : 'white')};
border-color: 'dark gray';
border-width: 1px;
border-style: solid;
border-radius: 2px;
${HiddenCheckbox}:focus + & {
box-shadow: 0 0 0 3px grey;
}
${Icon} {
visibility: ${(props: CheckboxProps) => (props.checked ? 'visible' : 'hidden')};
}
`;
const Checkbox: FunctionComponent<CheckboxProps> = ({ onChange, checked, ...props }) => {
return (
<CheckboxContainer>
<HiddenCheckbox checked={checked} {...props} onChange={onChange} />
<StyledCheckbox data-testid="styledcheckbox" checked={checked} {...props} onChange={onChange}>
<Icon viewBox="0 0 24 24">
<polyline points="20 6 9 17 4 12" />
</Icon>
</StyledCheckbox>
</CheckboxContainer>
);
};
export default Checkbox;
Checkbox.stories.js
// Checkbox.stories.js
import React, { useState, useRef } from 'react';
import Checkbox from '#components/Checkbox/Checkbox';
import { action } from '#storybook/addon-actions';
import { storiesOf } from '#storybook/react';
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const CheckboxStateful = props => {
const [value, setValue] = useState(props);
const valRef = useRef(value);
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const onChange = event => {
setValue(event.target.value);
valRef.current = event.target.value;
};
return (
<Checkbox
value={value}
onChange={event => {
onChange(event);
}}
></Checkbox>
);
};
storiesOf('Checkbox', module)
.add('with checked', () => {
const value = true;
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const onChange = event => setValue(event.target.value);
return <CheckboxStateful value={value} onChange={onChange}></CheckboxStateful>;
})
.add('with unchecked', () => {
const value = false;
// eslint-disable-next-line #typescript-eslint/explicit-function-return-type
const onChange = event => setValue(event.target.value);
return <CheckboxStateful value={value} onChange={onChange}></CheckboxStateful>;
});
As I am a novice at both React and Storybook I may require some expert input into whether my change handler code is correct or not. I have looked at other examples, but none of them use Typescript. Any help would be appreciated.
Solution is to change onChange to onClick in CheckboxProps in Checkbox.tsx, as follows:
onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
and to simplify CheckboxStateful in Checkbox.stories.js as follows:
const CheckboxStateful = () => {
const [value, setValue] = useState(false);
return <Checkbox checked={value} onClick={() => setValue(!value)}></Checkbox>;
};
The complete updated code is now as follows:
Checkbox.tsx
import * as React from 'react';
import styled from 'styled-components';
import { FunctionComponent } from 'react';
type CheckboxProps = {
checked?: boolean;
onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
};
const CheckboxContainer = styled.div`
display: inline-block;
vertical-align: middle;
`;
const Icon = styled.svg`
fill: none;
stroke: white;
stroke-width: 2px;
`;
const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
border: 0;
clip: rect(0 0 0 0);
clippath: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`;
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: ${(props: CheckboxProps) => (props.checked ? 'green' : 'white')};
border-color: 'dark gray';
border-width: 1px;
border-style: solid;
border-radius: 2px;
${HiddenCheckbox}:focus + & {
box-shadow: 0 0 0 3px grey;
}
${Icon} {
visibility: ${(props: CheckboxProps) => (props.checked ? 'visible' : 'hidden')};
}
`;
const Checkbox: FunctionComponent<CheckboxProps> = ({ onClick, checked, ...props }) => {
return (
<CheckboxContainer>
<HiddenCheckbox checked={checked} {...props} />
<StyledCheckbox checked={checked} {...props} onClick={onClick} data-testid="styledcheckbox">
<Icon viewBox="0 0 24 24">
<polyline points="20 6 9 17 4 12" />
</Icon>
</StyledCheckbox>
</CheckboxContainer>
);
};
export default Checkbox;
Checkbox.stories.js
/* eslint-disable #typescript-eslint/explicit-function-return-type */
// Checkbox.stories.js
import * as React from 'react';
import { useState } from 'react';
import Checkbox from '#components/Checkbox/Checkbox';
import { action } from '#storybook/addon-actions';
import { storiesOf } from '#storybook/react';
export default {
title: 'Checkbox',
component: Checkbox,
};
const CheckboxStateful = () => {
const [value, setValue] = useState(false);
return <Checkbox checked={value} onClick={() => setValue(!value)}></Checkbox>;
};
storiesOf('Checkbox', module)
.add('with checked', () => {
const value = true;
return <Checkbox checked={value} onClick={action('checked')}></Checkbox>;
})
.add('with unchecked', () => {
const value = false;
return <Checkbox checked={value} onClick={action('checked')}></Checkbox>;
})
.add('stateful', () => {
return <CheckboxStateful />;
});
Once you create the React project and add Storybook, just run yarn storybook and the checkboxes will show up correctly.
Instead of creating a new Stateful component a much better approach is to use state in the components template.
An example:
const Template: ComponentStory<typeof MyComponent> = (args) => {
const [state, setState] = useState('');
return (
<MyComponent
prop1={args.prop1}
prop2={args.prop2}
onChange={() => setState('Change')}
/>
);
};
Here's a link to the original answer I got this approach from:
https://stackoverflow.com/a/64722559/10641401

React Js Button Toggle/ Styled Components

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>
);
}

Resources