Custom HTML inside functional component - reactjs

I have simple functional component in react
import React from 'react';
import styled from 'styled-components';
import IconButton from '#material-ui/core/IconButton';
import AddShoppingCartIcon from '#material-ui/icons/AddShoppingCart';
import RemoveShoppingCartIcon from '#material-ui/icons/RemoveShoppingCart';
const Wrapper = styled.section`
padding: 1em;
margin: 0 auto;
width: 400px;
`;
const IngredientRow = styled.div`
display: flex;
margin: 5px 0;
width: 40%;
margin: 0 auto;
margin-bottom: 10px;
justify-content: space-between;
align-items: center;
`
const IngredientSelector = (props) => {
const Ingredient = (ingredient) => {
return (
<IngredientRow key={ingredient.id}>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<RemoveShoppingCartIcon />
</IconButton>
</span>
<h4>a</h4>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<AddShoppingCartIcon />
</IconButton>
</span>
</IngredientRow>
)
}
return (
<Wrapper>
<h2>Select the Burger Ingredients</h2>
{
props.ingredients.map(ingredient => {
<Ingredient ingredient={ingredient} />
})
}
</Wrapper>
)
}
export default IngredientSelector;
Its not working and giving error in console. Not sure why not working. Please help.

try moving the ingredient function outside of the ingredientSelector and add object destructuring to the props of the ingredient
const Ingredient = ({ingredient}) => {
return (
<IngredientRow key={ingredient.id}>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<RemoveShoppingCartIcon />
</IconButton>
</span>
<h4>a</h4>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<AddShoppingCartIcon />
</IconButton>
</span>
</IngredientRow>
)
}
const IngredientSelector = (props) => {
return (
<Wrapper>
<h2>Select the Burger Ingredients</h2>
{props.ingredients.map(ingredient => {
<Ingredient ingredient={ingredient} />
})
}
</Wrapper>
)
}

In the object "Ingredient" you should access the ingredientId using "props.ingredient.id" in your case "ingredient.indgredient.id". Because in the functional component "Ingredient". The variable passed is ingredient. Also, key should be passed in map function not in the return. So, your existing code would look like.
import React from 'react';
import styled from 'styled-components';
import IconButton from '#material-ui/core/IconButton';
import AddShoppingCartIcon from '#material-ui/icons/AddShoppingCart';
import RemoveShoppingCartIcon from '#material-ui/icons/RemoveShoppingCart';
const Wrapper = styled.section`
padding: 1em;
margin: 0 auto;
width: 400px;
`;
const IngredientRow = styled.div`
display: flex;
margin: 5px 0;
width: 40%;
margin: 0 auto;
margin-bottom: 10px;
justify-content: space-between;
align-items: center;
`
const IngredientSelector = (props) => {
const Ingredient = (ingredient) => { {/* change it props. But, its your preference*/}
return (
<IngredientRow key={ingredient.ingredient.id}> {/* Because the function component receives variable name ingredient`enter code here` */}
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<RemoveShoppingCartIcon />
</IconButton>
</span>
<h4>a</h4>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<AddShoppingCartIcon />
</IconButton>
</span>
</IngredientRow>
)
}
return (
<Wrapper>
<h2>Select the Burger Ingredients</h2>
{
props.ingredients.map(ingredient => {
<Ingredient ingredient={ingredient} key={ingredient.id}/> {/* Code change here */}
})
}
</Wrapper>
)
}
export default IngredientSelector;

Looks like you forgot to return the dom inside the map.
And you are passing ingredient to the Ingredient component as a prop not as an argument. Functional component receives props as an argument.
import React from 'react';
import styled from 'styled-components';
import IconButton from '#material-ui/core/IconButton';
import AddShoppingCartIcon from '#material-ui/icons/AddShoppingCart';
import RemoveShoppingCartIcon from '#material-ui/icons/RemoveShoppingCart';
const Wrapper = styled.section`
padding: 1em;
margin: 0 auto;
width: 400px;
`;
const IngredientRow = styled.div`
display: flex;
margin: 5px 0;
width: 40%;
margin: 0 auto;
margin-bottom: 10px;
justify-content: space-between;
align-items: center;
`
const IngredientSelector = (props) => {
const Ingredient = ({ingredient}) => { // recieves props
return (
<IngredientRow key={ingredient.id}>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<RemoveShoppingCartIcon />
</IconButton>
</span>
<h4>a</h4>
<span>
<IconButton color="primary" aria-label="add to shopping cart">
<AddShoppingCartIcon />
</IconButton>
</span>
</IngredientRow>
)
}
return (
<Wrapper>
<h2>Select the Burger Ingredients</h2>
{
props.ingredients.map(ingredient => {
return <Ingredient ingredient={ingredient} /> // return the dom
})
}
</Wrapper>
)
}
export default IngredientSelector;

Related

Close mobile navigation menu after anchor link click - React

I am new to React and am trying to create a mobile menu that I somehow created using several online resources. Now the problem is that my mobile menu is not closing after clicking on the Anchor links.
I have three different files (Burger, Navbar, and RightNav). Burger is just for icon transformation, Navbar is for main navigation, and RightNav is the mobile menu.
Can someone please specify how I can close the RightNav after clicking on any menu anchor link?
Burger.jsx file
import React, { useState } from 'react';
import styled from 'styled-components';
import RightNav from './RightNav';
const StyledBurger = styled.div`
width: 2rem;
height: 2rem;
position: fixed;
align-items:center;
right: 20px;
z-index: 20;
display: none;
top: 5%;
#media (max-width: 1000px) {
display: flex;
justify-content: space-around;
flex-flow: column nowrap;
align-items:flex-start;
}
div {
width: 2rem;
height: 0.25rem;
background-color: ${({ open }) => open ? '#00a993' : '#00a993'};
border-radius: 10px;
transform-origin: 1px;
transition: all 0.3s linear;
&:nth-child(1) {
transform: ${({ open }) => open ? 'rotate(45deg)' : 'rotate(0)' };
}
&:nth-child(2) {
transform: ${({ open }) => open ? 'translateX(100%)' : 'translateX(0)'};
opacity: ${({ open }) => open ? 0 : 1};
}
&:nth-child(3) {
transform: ${({ open }) => open ? 'rotate(-45deg)' : 'rotate(0)'};
width: ${({ open }) => open ? '20px':'16px'};
}
}
`;
const Burger = () => {
const [open, setOpen] = useState(false)
return (
<>
<StyledBurger open={open} onClick={() => setOpen(!open)} id="burger" className='burger'>
<div />
<div />
<div />
</StyledBurger>
<RightNav open={open}/>
</>
)
}
export default Burger
RightNav.jsx file
import React from 'react';
import styled from 'styled-components';
import { NavHashLink } from 'react-router-hash-link';
const Ul = styled.ul`
list-style: none;
display: flex;
flex-flow: row nowrap;
li {
padding: 18px 10px;
}
#media (max-width: 1000px) {
z-index: 1;
flex-flow: column nowrap;
background-color: #13161B;
position: fixed;
transform: ${({ open }) => open ? 'translateX(0)' : 'translateX(100%)'};
top: 10%;
right: 0;
width: 100%;
height:auto;
padding: 1.5rem;
align-items: center;
transition: transform 0.3s ease-in-out;
font-size: 16px;
font-family: 'Quicksand', sans-serif;
text-transform: uppercase;
font-weight: 600;
line-height: 7rem;
}
`;
const RightNav = ({ open, setOpen }) => {
return (
<Ul open={open} className="mobile-menu" id="mobile-menu">
<NavHashLink smooth to="/#welcome-section" activeClassName="selected" style={{ color: '#00a993' }}>
What
</NavHashLink>
<NavHashLink smooth to="/#how-section" activeClassName="selected" style={{ color: '#00a993' }}>
How
</NavHashLink>
<NavHashLink smooth to="/#IsItAThing-section" activeClassName="selected" style={{ color: '#00a993' }}>
Why
</NavHashLink>
<NavHashLink smooth to="/#team-section" activeClassName="selected" style={{ color: '#00a993' }}>
Who
</NavHashLink>
<NavHashLink smooth to="/#contact-section" activeClassName="selected" style={{ color: '#00a993' }}>
Where
</NavHashLink>
<a href='https://stories.therelevancehouse.com/' style={{ color: '#00a993' }}>
Stories
</a>
</Ul>
)
}
export default RightNav
Navbar.jsx file
import styled from 'styled-components';
import Burger from './Burger';
import React, { useEffect, useState } from 'react';
import '../App.css';
import logo from '../images/logomain.png'
import ln from '../images/LinkedIN_white.svg'
import medium from '../images/Medium_white.svg'
import twitter from '../images/Twitter_white.svg'
import fb from '../images/Facebook_white.svg'
import instagram from '../images/Instagram_white.svg'
import '../fonts/norwester.ttf'
import linkedinhover from '../images/LinkedinIconHover.svg'
import facebookhover from '../images/FacebookIconHover.svg'
import twitterhover from '../images/TwitterIconHover.svg'
import instagramhover from '../images/InstagramIconHover.svg'
import mediumhover from '../images/MediumIconHover.svg'
import { NavHashLink } from 'react-router-hash-link'
const Nav = styled.nav`
width: 100%;
border-bottom: 2px solid #f1f1f1;
padding: 20px 20px;
display: flex;
justify-content: space-between;
align-items: center;
.logo {
padding: 15px 0;
}
`
const Navbar = () => {
const [show, setShow] = useState(true);
const [lastScrollY, setLastScrollY] = useState(0);
useEffect(() => {
const controlNavbar = () => {
if (typeof window !== 'undefined') {
if ((window.scrollY > lastScrollY) && (window.scrollY > 200) && (window.innerWidth > 1000))
{
// document.getElementById("navbar").style.padding = "30px 10px";
document.getElementById("navtext").style.fontSize = "12px";
document.getElementById("navtext1").style.fontSize = "12px";
document.getElementById("navtext2").style.fontSize = "12px";
document.getElementById("navtext3").style.fontSize = "12px";
document.getElementById("navtext4").style.fontSize = "12px";
document.getElementById("navtext5").style.fontSize = "12px";
document.getElementById("nav-bar-logo1").style.height = "80px";
document.getElementById("nav").style.height = "90px";
// setShow(false);
} else {
setShow(true);
document.getElementById("navtext").style.fontSize = "14px";
document.getElementById("navtext1").style.fontSize = "14px";
document.getElementById("navtext2").style.fontSize = "14px";
document.getElementById("navtext3").style.fontSize = "14px";
document.getElementById("navtext4").style.fontSize = "14px";
document.getElementById("navtext5").style.fontSize = "14px";
document.getElementById("nav-bar-logo1").style.height = "105px";
document.getElementById("nav").style.height = "120px";
}
setLastScrollY(window.scrollY);
}
};
if (typeof window !== 'undefined') {
window.addEventListener('scroll', controlNavbar);
return () => {
window.removeEventListener('scroll', controlNavbar);
};
}
}, [lastScrollY]);
return (
<Nav >
<nav id='nav' className={ `${show && 'nav'}`} >
<a href="./" >
<img id='nav-bar-logo1' className='nav-bar-logo' alt="Marketing, Branding and Communications Agency"
src={logo} />
</a>
<div id='navigation-social'>
<a href='/'>
<img className='social-nav' src={ln} alt='linkedin' onMouseOver={e => e.currentTarget.src = linkedinhover} onMouseOut={e => e.currentTarget.src = ln} />
</a>
<a href='/'>
<img className='social-nav' src={medium} alt='medium' onMouseOver={e => e.currentTarget.src = mediumhover} onMouseOut={e => e.currentTarget.src = medium} />
</a>
<a href='/'>
<img className='social-nav' src={twitter} alt='twitter' onMouseOver={e => e.currentTarget.src = twitterhover} onMouseOut={e => e.currentTarget.src = twitter} />
</a>
<a href='/'>
<img className='social-nav' src={fb} alt='facebook' onMouseOver={e => e.currentTarget.src = facebookhover} onMouseOut={e => e.currentTarget.src = fb} />
</a>
<a href='/'>
<img className='social-nav' src={instagram} alt='instagram' onMouseOver={e => e.currentTarget.src = instagramhover} onMouseOut={e => e.currentTarget.src = instagram} />
</a>
</div>
<div id='navigation-items'>
<NavHashLink id="navtext" smooth to="/#welcome-section" activeClassName="selected" activeStyle={{ color: '#00a993' }}>
What
</NavHashLink>
<NavHashLink id="navtext1" smooth to="/#how-section" activeClassName="selected" activeStyle={{ color: '#00a993' }}>
How
</NavHashLink>
<NavHashLink id="navtext2" smooth to="/#IsItAThing-section" activeClassName="selected" activeStyle={{ color: '#00a993' }}>
Why
</NavHashLink>
<NavHashLink id="navtext3" smooth to="/#team-section" activeClassName="selected" activeStyle={{ color: '#00a993' }}>
Who
</NavHashLink>
<NavHashLink id="navtext4" smooth to="/#contact-section" activeClassName="selected" activeStyle={{ color: '#00a993' }}>
Where
</NavHashLink>
<a id="navtext5" href='https://stories.therelevancehouse.com/'>
Stories
</a>
</div>
</nav>
<Burger />
</Nav>
)
}
export default Navbar

Unable to view custom sidebar component created with react router in storybook

I am quite new to React and have been trying to create a custom sidebar component. Based on my understanding by going through different tutorials, this is what I have so far:
react-router-dom version: ^6.3.0
storybook: ^6.1.17
These are my components
sidebar.jsx
const SidebarNav = styled.nav`
background: #15171c;
width: 250px;
height: 100vh;
display: flex;
justify-content: center;
position: fixed;
top: 0;
right: ${({ sidebar }) => (sidebar ? "0" : "-100%")};
transition: 350ms;
z-index: 10;
`;
const SidebarWrap = styled.div`
width: 100%;
`;
const Sidebar = () => {
const [sidebar, setSidebar] = useState(true);
return (
<>
<IconContext.Provider value={{ color: "#fff" }}>
<SidebarNav sidebar={sidebar}>
<SidebarWrap>
{sideBarData.map((item, index) => {
return <SubMenu item={item} key={index} />;
})}
</SidebarWrap>
</SidebarNav>
</IconContext.Provider>
</>
);
};
export default Sidebar;
sidebardata.jsx
export const sideBarData = [ {
title: "Question 4",
path: "/test"
},
// {
// title: "Question 5",
// path: "/testing"
// }
];
sidebarsubmenu.jsx
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const SidebarLink = styled(Link)`
display: flex;
color: #e1e9fc;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid green;
cursor: pointer;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
const DropdownLink = styled(Link)`
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: #f5f5f5;
font-size: 18px;
&:hover {
background: green;
cursor: pointer;
}
`;
const SubMenu = ({ item }) => {
const [subnav, setSubnav] = useState(false);
const showSubnav = () => setSubnav(!subnav);
return (
<>
<SidebarLink to={item.path}
onClick={item.subNav && showSubnav}>
<div>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</div>
<div>
{item.subNav && subnav
? item.iconOpened
: item.subNav
? item.iconClosed
: null}
</div>
</SidebarLink>
{subnav &&
item.subNav.map((item, index) => {
return (
<DropdownLink to={item.path} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
BookSideBarLayout.jsx
import React from 'react';
import Sidebar from './BookSideBar';
function Layout(props) {
return (
<div>
<div style={{display: "flex"}}>
<Sidebar/>
</div>
</div>
);
}
export default Layout;
BookSidebarRoutes.jsx
import React from "react";
import {BrowserRouter, Route,Routes} from "react-router-dom";
import Support from "./Support";
import Layout from "./BookSideBarLayout";
function MyRoutes() {
return (
<BrowserRouter>
<Layout/>
<Routes>
{/* <Route path="/" element={<Sidebar/>}/> */}
<Route path="/test" element={<Support/>}/>
</Routes>
</BrowserRouter>
);
}
export default MyRoutes;
The story that I created was as below
import { storiesOf } from '#storybook/react';
import StoryRouter from 'storybook-react-router';
import MyRoutes from '../../src/components/BookSidebarRoutes';
// export default {
// title: 'Side Bar',
// //decorators : [(Story) => (<MemoryRouter><Story/></MemoryRouter>)]
// };
storiesOf('Params', module)
.addDecorator(StoryRouter())
.add('params', () => (
<MyRoutes/>
));
export const Default = () => <MyRoutes />;
After I run my story above, I keep getting the error as below:
A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.**
I also tried Switch in the MyRoutes component but that too didn't work. Any guidance in this will be really helpful. Thank you in advance

White screen when createPage with variable in Gatsby

I'm trying to create dynamic product page in Gatsby.
Here is what I've edited in file gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
const { createRedirect } = actions
const { createPage } = actions
const {
data: { products },
} = await graphql(`
{
products: allTestJson {
edges {
node {
brand
category
}
}
}
}
`)
const nodeArray = products.edges.map(edge => edge.node.brand)
const unique = [...new Set(nodeArray)]
console.log("UNIQUE")
console.log(unique)
unique.forEach((brand) => {
console.log("BRAND")
console.log(brand)
createPage({
path: `/products/${brand}`,
component: require.resolve(`./src/pages/contact-us.js`),
})
})
}
Here I do the console.log and I'm sure the ${node.brand} returns a value. (One of it is "faro"). However, when I visit /products/faro on the browser, I only see a big white blank page:
When I change the path to path: "path: /products/faro" (just hard code path as products/faro), then the page component renders correctly.
Is there something wrong with the loop or variable? I'm following this tutorial https://dev.to/notrab/build-static-product-pages-with-gatsby-and-commerce-js-3952
import React, { Component } from "react"
import styled from "styled-components"
import { injectIntl, Link, FormattedMessage } from "gatsby-plugin-intl"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { Background } from "../components/layout/Background"
import { Space } from "../components/layout/Space"
import ShowroomGrid from "../components/organisms/ShowroomGrid"
import ContactForm from "../components/organisms/ContactForm"
import Map from "../components/molecules/Map"
class ContactUs extends Component {
state = {
responsiveSize: null,
}
componentDidMount = () => {
this.setState({
responsiveSize: window.innerWidth,
})
}
render() {
const { responsiveSize } = this.state
return (
<Layout>
<SEO
description="Visit our showrooms in Ho Chi Minh City & Hanoi to admire the timeless masterpieces from Fritz Hansen, Flos, Louis Poulsen, Fontana Arte & Faro."
title="Contact Us - nanoHome showrooms in HCM & Hanoi"
keywords={[`luxury`, `lighting`, `furniture`, `showroom`]}
/>
<Background>
<MainContainer className="main-container">
<Space height="64" />
<H1Tag>nanoHome | Contact Us</H1Tag>
<SectionMapAndContact responsiveSize={responsiveSize}>
<GoogleMap>
<Map />
</GoogleMap>
<Contacts>
<Title>
<FormattedMessage id="page-contact-us.section-map-and-contact.title" />
</Title>
<Space height="48" />
<Address>
<b>
<FormattedMessage id="page-contact-us.section-map-and-contact.office" /> </b> <Space height="16" />
<p>
<FormattedMessage id="page-contact-us.section-map-and-contact.address" />
</p>
<Space height="16" />
<p>‭+84 90 984 0028</p>
<Space height="8" />
<p>info#nanohome.vn</p>
<Space height="8" />
<p>www.nanohome.vn</p>
</Address>
</Contacts>
</SectionMapAndContact>
<Space height="64" />
<SectionShowrooms>
<Title>Our Showrooms</Title>
<Space height="48" />
<ShowroomGrid responsiveSize={responsiveSize} />
</SectionShowrooms>
<Space height="64" />
<SectionContactForm responsiveSize={responsiveSize}>
<Title>Get in Touch</Title>
<Space height="48" />
<ContactForm />
</SectionContactForm>
</MainContainer>
</Background>
</Layout>
)
}
}
const MainContainer = styled.div`
margin: 0 auto;
`
const SectionMapAndContact = styled.div`
max-width: 960px;
margin: auto;
padding: 0 2rem;
display: grid;
grid-template-columns: ${props =>
props.responsiveSize > 1024 ? "1fr 1fr" : "1fr"};
grid-template-rows: ${props =>
props.responsiveSize < 1024 ? "1fr auto" : "1fr"};
align-items: center;
justify-content: center;
grid-gap: ${props => (props.responsiveSize > 1024 ? "4rem" : "2rem")};
`
const GoogleMap = styled.div`
width: 100%;
height: 500px;
`
const Contacts = styled.div`
width: 100%;
max-width: 440px;
`
const Title = styled.h2`
font-size: var(--font-size-larger);
color: var(--color-primary-blue);
font-family: "Miller";
`
const Address = styled.div`
color: var(--color-primary-blue);
font-size: var(--font-size-large);
border-left: 2px solid var(--color-primary-blue);
padding: 0 2rem;
p {
font-size: var(--font-size-medium);
line-height: 1.5;
margin: 0;
}
`
const SectionShowrooms = styled.div`
padding: 0 2rem;
max-width: 960px;
margin: auto;
`
const SectionContactForm = styled.div`
width: ${props =>
(props.responsiveSize > 1024 && "24%") ||
(props.responsiveSize > 768 && "50%") ||
(props.responsiveSize > 480 && "80%") ||
"100%"};
margin: auto;
max-width: 960px;
padding: 0 2rem;
min-width: 320px;
display: flex;
flex-direction: column;
align-items: center;
`
const H1Tag = styled.h1`
visibility: hidden;
font-size: var(--font-size-smaller);
`
export default ContactUs
I also add the debug of unique Array and brand here (sorry it's quite messy as I'm outside)
The fact that you're not getting a 404 page indicates that your page is being created correctly.
Is it possible the page is expecting some sort of props? In the tutorial you linked, it looks like they are passing an id prop. To pass such props you need to pass a context arg to createPage (eg. createPage({ component, path, context: { id })) ... but the code you provided doesn't have any.
Whatever the problem is, it seems likely that it's in contact-us.js, so you'll need to debug it.
The problem is the "brand data" that I loaded from graphql has some capital letters, and the browser always lowercase text, so it does not match the path in gatsby-node.js. Thank you #harley for spotting this.
Thank you for all your help.

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

How to create Accordion using Card Component

I am using react-bootstrap 1.0.0-beta.3, which is build for supporting bootstrap 4 update.
Before this I was using react-bootstrap 0.32.1 and created Accordion using Panels and Panel group.
But after bootstrap upgrade it was suggested to Card component. I tried to achieve the same behavior like this:
<CardGroup>
<Card eventKey={this.state.eventKey} className="border-0">
<Card.Header>
<div className="row">
<div className="col-xs-9 col-sm-9 col-md-9 col-lg-9">
<Card.Title>
This is test
</Card.Title>
</div>
<div className="col-xs-3 col-sm-3 col-md-3 col-lg-3">
Test Text 123
</div>
</div>
</Card.Header>
<Card.Body>
Test Text 456
</Card.Body>
</Card>
</CardGroup>
I am facing couple of issues here:
How to make one card to take the full width.
How to make this structure behave like accordion.
Something like this:
You'll need to create custom components and css classNames.
Working example: https://codesandbox.io/s/8zkrw9jw50
components/Accordian.js
import React from "react";
import Card from "../../components/Card";
const panels = [
"Add Edit Menus",
"Resource Management",
"Asset Management",
"User Management",
"Account Management"
];
export default () => (
<div className="app-container">
<div className="accordion-container">
{panels.map(title => (
<Card key={title} title={title} />
))}
</div>
</div>
);
components/Card.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Row, Col, Card } from "react-bootstrap";
import Collapse from "../Collapse";
import Button from "../Button";
const { Body, Header, Title } = Card;
class CardComponent extends Component {
state = { isActive: false };
toggleVisibility = () =>
this.setState(prevState => ({ isActive: !this.state.isActive }));
render = () => (
<div className={`${this.state.isActive ? "active" : "inactive"}`}>
<Card>
<Header style={{ padding: 0 }}>
<Row>
<Col xs={9}>
<Button onClick={this.toggleVisibility}>
{!this.state.isActive ? "+" : "-"}
</Button>
<Title style={{ display: "inline-block" }}>
{this.props.title}
</Title>
</Col>
<Col style={{ paddingTop: 7 }} xs={3}>
Test Text 123
</Col>
</Row>
</Header>
<Collapse>
<Body style={{ padding: 10 }}>Test Text 456</Body>
</Collapse>
</Card>
</div>
);
}
export default CardComponent;
CardComponent.propTypes = {
title: PropTypes.string.isRequired
};
components/Button.js
import styled from "styled-components";
const StyledButton = styled.button`
color: #909090;
background-color: transparent;
font-weight: bold;
outline: none;
border: 0;
cursor: pointer;
font-size: 22px;
transition: all 0.3s ease-in-out;
margin: 0 15px;
width: 25px;
&:hover {
color: #333333;
}
&:focus {
outline: none;
}
`;
export default StyledButton;
components/Collapse.js
import React from "react";
import PropTypes from "prop-types";
const Collapse = ({ children }) => (
<span className="folding-pannel">{children}</span>
);
export default Collapse;
Collapse.propTypes = {
children: PropTypes.node.isRequired
};
styles.css
.accordion-container {
width: 100%;
}
.app-container {
margin: 20px;
}
.active,
.inactive {
margin-bottom: 5px;
}
.active .folding-pannel {
transition: all 0.3s ease-in-out;
height: 42px;
}
.inactive .folding-pannel {
transform: perspective(0px) rotateX(90deg);
transition: all 0.3s ease-in-out;
height: 0;
}

Resources