I want to load a component depending in what page are the user.
Pages:
Executables
Shop
In the main screen I have a sidebar with 2 icons that i want the primary button sets the Executables Page and the second shop page.
Like having a web page with no routes and rendering components depending the user selection.
My code:
Components/Dashboard.tsx
import styled from "styled-components"
import Executable from "./Executable"
import Navbar from "./Navbar"
import { useEffect } from "react"
type EntryProps = {
section: string
}
const Dashboard = ({ section }: EntryProps) => {
var TypeElement
useEffect(() => {
if (section === "executables") {
TypeElement = (
<div className="grid">
<div className="row">
<Executable />
</div>
</div>
)
}
}, [section])
return (
<Section>
<Navbar />
{TypeElement}
</Section>
)
}
Components/Sidebar.tsx
import styled from "styled-components"
import { FaBars } from "react-icons/fa"
import { BsFileEarmarkBinary } from "react-icons/bs"
import { BiLogOut } from "react-icons/bi"
import { AiOutlineShoppingCart } from "react-icons/ai"
import { IoMdSettings } from "react-icons/io"
import { useState } from "react"
type SidebarProps = {
section: string
setSection: Function
}
const Sidebar = ({ section, setSection }: SidebarProps) => {
const [disabled, setDisabled] = useState(true)
const handleDisabled = () => setDisabled(!disabled)
return (
<Aside id="aside">
<div
className={disabled ? "brand center" : "brand"}
onClick={handleDisabled}
>
<FaBars />
</div>
<ul className="links">
<li>
<BsFileEarmarkBinary />
<span
className={disabled ? "disabled" : ""}
onClick={(e) => {
console.log("Executables")
setSection("executables")
}}
>
Executables
</span>
</li>
<li>
<AiOutlineShoppingCart />
<span
className={disabled ? "disabled" : ""}
onClick={(e) => {
e.preventDefault()
setSection("shop")
}}
>
Shop
</span>
</li>
<li>
<IoMdSettings />
<span
className={disabled ? "disabled" : ""}
onClick={setSection("settings")}
>
Settings
</span>
</li>
</ul>
<div className="logout">
<BiLogOut />
</div>
</Aside>
)
}
Pages/DashboardPage.tsx
import styled from "styled-components"
// Components
import Sidebar from "../components/Sidebar"
import Rightsidebar from "../components/Rightsidebar"
import Dashboard from "../components/Dashboard"
import { useState } from "react"
const DashboardPage = () => {
const [page, setPage] = useState("executables")
const setSection = (name: string) => {
setPage(name)
}
return (
<Div>
<Sidebar section={page} setSection={setSection} />
<Dashboard section={page} />
<Rightsidebar />
</Div>
)
}
You are changing the value of TypeElement conditionally according to the value of the section. TypeElement is not a state, so after changing the value of the TypeElement component is not rerendered, and the updated value is not showing on the UI. Here conditional rendering might be a good solution.
<Section>
<Navbar />
{section==='executables'? <Executable />: <Shop/>}
</Section>
Related
I need to parse data from modal window (Modalbuy.js) to parent component (Page.js) and then to another child, but my Context Provider doesn't see changed data.Despite that fact, if I ask to console.log not in context.provider brackets, but still in parent component, I get my data logs right
Modalbuy.js
import React, {useState} from "react";
import "./modal.css";
import { DataBuyContext } from "../../page/page";
const Modalbuy = ({active, setActive,price}) => {
const [inputVolume, setInputVolume] = useState("")
function saveInput(event) {
setInputVolume(event.target.value)
console.log(inputVolume)
}
const {dataBuy, setDataBuy} = React.useContext(DataBuyContext)
function addBuy() {
setDataBuy([...dataBuy,{side: "BUY", price:{price},volume: {inputVolume},timestamp: new Date().toLocaleTimeString()}])
// console.log(dataBuy)
}
return (
<div className={active ? "modal active" : "modal"} onClick={() => setActive(false)}>
<div className="modal__content" onClick={e => e.stopPropagation()}>
<header>Make order</header>
<p>BUY {price}</p>
<input placeholder="Volume" value={inputVolume} onChange={saveInput}></input>
<div>
<button onClick = {addBuy}>Ok</button>
<button onClick={() => setActive(false)} >Cancel</button>
</div>
</div>
</div>
)
}
export default Modalbuy;
Page.js
import React, {useState} from 'react'
import Trading from '../trading/Trading'
import Archive from '../archive/Archive'
import './page.css';
export const DataBuyContext = React.createContext({})
const Page = () => {
const [dataBuy, setDataBuy] = useState([{}])
const [toggleState, setToggleState] = useState(1)
const toggleTab = (index) =>{
setToggleState(index);
}
console.log(dataBuy)
return (
<DataBuyContext.Provider value = {{dataBuy, setDataBuy}}>
<div className="container">
<div className="block-tabs">
<button
className={toggleState === 1 ? "tabs active-tabs" : "tabs"}
onClick={() => toggleTab(1)}>
Trading
</button>
<button
className={toggleState === 2 ? "tabs active-tabs" : "tabs"}
onClick={() => toggleTab(2)}>
Archive
</button>
</div>
<div className="content-tabs">
<div
className={toggleState === 1 ? "content active-content" : "content"}>
<Trading />
</div>
<div
className={toggleState === 2 ? "content active-content" : "content"}>
<Archive dataBuy= {dataBuy} />
</div>
</div>
</div>
</DataBuyContext.Provider>
);
}
export default Page;
And Archive.js child component, where I want to transport data
import React, {useState} from 'react';
import './archive.css';
import Table from './Table';
const Archive = (dataBuy) => {
const [rows, setRows] = useState(dataBuy)
console.log(rows)
return (
<Table dataBuy = {rows}/>
)
}
export default Archive;
App.js
import './App.css';
import React from 'react';
import Page from './components/page/page'
function App() {
return (
<div className="App">
<Page/>
</div>
);
}
export default App;
I have a functioning "dropdown menu" - clicking on "menu item" routes to a different component. I want to change the style of "dropdown menu item" after clicking, so the next time "dropdown menu" opens, "item" that has been clicked has a different look - indicating active state.
Behavior I want to replicate can be observed on the page I'm re-writing in React : code-learning.uk , after clicking on item from black menu dropdown button (USEFUL ST), "menu item" changes color to blue.
DropdownMenu
import { useState } from "react"
import { MenuItemContentSchool } from "./sub-components/MenuItemContentSchool"
import { Link } from "react-router-dom";
const DropdownMenu = () => {
const [click, setClick] = useState("");
const handleClick = () => setClick("hide-menu");
return (
<div className={`${click}`}>
{MenuItemContentSchool.map((item, index) => {
return (
<Link to={item.link} onClick={handleClick} key={item.title}>
{item.title}
</Link>
)
} )}
</div>
)
}
export default DropdownMenu
NaviMain.js
import { useState } from "react"
import DropdownMenu from "./DropdownMenu";
const NaviMain = () => {
const [disp, setDisp] = useState(false);
const hoverOn = () => setDisp(true)
const hoverOff = () => setDisp(false)
return (
<nav>
<ul>
<li onMouseEnter={hoverOn} onMouseLeave={hoverOff}>
<a>school</a>
{ disp && <DropdownMenu /> }
</li>
</ul>
</nav>
)
}
export default NaviMain
App.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import NaviMain from "./components/NaviMain";
import VerticalAlign from "./pages/VerticalAlign";
import Flexbox from "./pages/Flexbox";
function App() {
return (
<Router>
<div className="App">
<NaviMain />
<Routes>
<Route path="/verticalalign" element={<VerticalAlign />} />
<Route path="/flexbox" element={<Flexbox/>} />
</Routes>
</div>
</Router>
);
}
export default App;
MenuItemContentSchool
export const MenuItemContentSchool = [
{
title:"v align",
link:"/verticalalign",
},
{
title:"flexbox",
link:"/flexbox",
},
]
Do a check to see if the current page matches the items link, then add a class to the link for that item.
something like:
const activeClass = window.location.pathname.startsWith(item.link) ? "isActive" : "";
<Link to={item.link} onClick={handleClick} key={item.title} className={activeClass}>
{item.title}
</Link>
i'm just starting to learn react, where did i go wrong, can't undestand what am i doing wrong this is my problem
my goal: to ensure that the picture changes as true / false
maybe I am not passing props correctly??
it's my code:
import React, { useState, useEffect } from 'react'
import styles from './styles.module.scss'
import { Link } from 'react-router-dom'
import classNames from 'classnames'
import DjalKokildak from '../../../../assets/images/DjalKokildak.png'
import Turpachaty from '../../../../assets/images/Turpachaty.png'
const Fields = ({image}) => {
const data = [
{
img: {
true : DjalKokildak,
false : Turpachaty
}
}
]
console.log(data)
const [image, setImage] = useState(true)
return (
<div className={styles.container}>
<div className={styles.wrapper}>
<div className={styles.line} />
<div className={styles.contentBlock}>
<div className={styles.titleBlock}>
<h1 className={styles.title}>месторождения</h1>
<p className={styles.text}>“Джал-Кокильдак” и “Турпачаты”</p>
<Link to='/' className={styles.link}>подробнее</Link>
</div>
<div className={styles.actionBlock}>
<button onClick={() => setImage(false)} className={styles.button}>след</button>
<div className={styles.imgBlock}>
{data.map(item => item.img === img && (
<img src={item.img[setImage]}>{image}</img>
))
}
</div>
<button onClick={() => setImage(true)} className={styles.button}>пред</button>
</div>
</div>
</div>
</div>
)
}
export default Fields
Story
I'm using this react.js template http://skote-v-light.react.themesbrand.com/email-read from https://themeforest.net/item/skote-react-admin-dashboard-template/26318700 for building my react app.
I use and modify the sidebar component from the template and name it Sidebar.js.
There is SidebarContent.js for building the content inside the sidebar.
There is MenuComponent.js inside SidebarContent.js for building the menu inside the sidebar.
MenuComponent.js takes a list from menuList.js and renders it.
There are two dummy pages which are Route/Summary page (Summary.js) and Payload page (Payload.js) inside the Sidebar. I combine them all inside PageWithSidebar.js.
Question
Why don't always the ABC, DEF, etc folders in the sidebar open when I click them?
Ideal Condition I Wanted
Every folder (ABC, DEF, etc) inside the sidebar always opens when I click one of them.
Actual Condition
Please watch this demo to understand the context: https://drive.google.com/file/d/1Lig1SKKRtyRqxWm_OYNkuQennrytV0jo/view?usp=sharing
Steps:
Click the Route/Summary option inside the ABC folder -> Summary page is shown (as I wanted)
Click the Payload option inside the ABC folder -> Payload page is shown (as I wanted)
Click another Route/Summary option inside the ABC folder -> Summary page is shown (as I wanted)
Close the ABC folder -> The folder closes (as I wanted)
Click the DEF folder -> The folder doesn't open (not I wanted)
There is no error or warning log in the browser console.
Code
PageWithSidebar.js:
import React from 'react'
import { Route, Switch } from "react-router-dom";
// COMPONENTS
import Sidebar from '../../components/Menu/Sidebar/Sidebar'
// PAGES
// ROUTE
import Summary from '../Routes/Summary/Summary'
// PAYLOAD
import Payload from '../Payload/Payload'
const PageWithSidebar = () => {
return(
<div id="layout-wrapper">
<Sidebar/>
<div className="main-content">
<Switch>
{/* ROUTE */}
<Route path="/route/summary/location=:locationName">
<Summary/>
</Route>
<Route path="/payload/location=:locationName">
<Payload/>
</Route>
</Switch>
</div>
</div>
)
}
export default PageWithSidebar
Sidebar.js:
import React from "react"
import { Link } from "react-router-dom"
// COMPONENTS
import SidebarContent from './SidebarContent'
// IMAGES
import IconClose from '../../../images/icons/close.svg'
import IconOpen from '../../../images/icons/open.svg'
const Sidebar = () => {
const toogleSidebar = () => {
var body = document.body;
body.classList.toggle("vertical-collpsed");
body.classList.toggle("sidebar-enable");
}
return (
<React.Fragment>
<div className="vertical-menu">
<div className="navbar-brand-box">
<Link to='/' className="logo logo-dark">
{/* SIDEBAR IS CLOSED */}
<span className="logo-sm">
{/* TOOGLE SIDEBAR BUTTON */}
<img src={IconOpen} alt='' className='icon-open' onClick={toogleSidebar}/>
</span>
{/* SIDEBAR IS OPEN */}
<span className="logo-lg sidebar-is-open-container">
{/* TOOGLE SIDEBAR BUTTON */}
<img src={IconClose} alt='' className='icon-close' onClick={toogleSidebar}/>
</span>
</Link>
</div>
{/* SIDEBAR CONTENT */}
<SidebarContent />
<div className="sidebar-background"></div>
</div>
</React.Fragment>
)
}
export default Sidebar
SidebarContent.js:
import PropTypes from "prop-types"
import React, { useEffect, useRef, useState } from "react"
import { withRouter, Link, useLocation } from "react-router-dom"
// Import Scrollbar
import SimpleBar from "simplebar-react"
// MetisMenu
import MetisMenu from "metismenujs"
//i18n
import { withTranslation } from "react-i18next"
// COMPONENTS
import MenuComponent from './MenuComponent'
// IMAGES
import IconFolderGray from '../../../images/icons/folder-gray.svg'
import IconFolderRed from '../../../images/icons/folder-red.svg'
import IconLogout from '../../../images/icons/logout.svg'
// MENU ITEMS
import menuList from './menuList/menuList'
// STYLES
import './styles.scss'
import logoutUser from '../../../utils/authentication/logOut'
const SidebarContent = props => {
// MY OWN CODE SECTION
const location = useLocation()
const pathName = location['pathname']
let locationName = ''
if(pathName.includes('route')) {
locationName = pathName.split('/')[3].replace('location=', '')
}
else {
locationName = pathName.split('/')[2].replace('location=', '')
}
// console.log('SidebarContent, locationName:', locationName)
// TEMPLATE CODE SECTION
const ref = useRef()
useEffect(() => {
const pathName = props.location.pathname
const initMenu = () => {
new MetisMenu("#side-menu")
let matchingMenuItem = null
const ul = document.getElementById("side-menu")
const items = ul.getElementsByTagName("a")
for (let i = 0; i < items.length; ++i) {
if (pathName === items[i].pathname) {
matchingMenuItem = items[i]
break
}
}
if (matchingMenuItem) {
activateParentDropdown(matchingMenuItem)
}
}
initMenu()
}, [props.location.pathname])
useEffect(() => {
ref.current.recalculate()
})
function scrollElement(item) {
if (item) {
const currentPosition = item.offsetTop
if (currentPosition > window.innerHeight) {
ref.current.getScrollElement().scrollTop = currentPosition - 300
}
}
}
function activateParentDropdown(item) {
item.classList.add("active")
const parent = item.parentElement
const parent2El = parent.childNodes[1]
if (parent2El && parent2El.id !== "side-menu") {
parent2El.classList.add("mm-show")
}
if (parent) {
parent.classList.add("mm-active")
const parent2 = parent.parentElement
if (parent2) {
parent2.classList.add("mm-show") // ul tag
const parent3 = parent2.parentElement // li tag
if (parent3) {
parent3.classList.add("mm-active") // li
parent3.childNodes[0].classList.add("mm-active") //a
const parent4 = parent3.parentElement // ul
if (parent4) {
parent4.classList.add("mm-show") // ul
const parent5 = parent4.parentElement
if (parent5) {
parent5.classList.add("mm-show") // li
parent5.childNodes[0].classList.add("mm-active") // a tag
}
}
}
}
scrollElement(item);
return false
}
scrollElement(item);
return false
}
// MY OWN CODE SECTION
const itemList = ['ABC', 'DEF', 'GHI', 'JKL', 'MNO', 'PQR']
const [ selectedItem, setSelectedItem ] = useState(itemList.indexOf(locationName.toUpperCase()))
return (
<React.Fragment>
<SimpleBar style={{ maxHeight: "100%" }} ref={ref}>
<div id="sidebar-menu">
<ul className="metismenu list-unstyled sidebar-content-menu-container" id="side-menu">
{/* TITLE */}
<li className="menu-title sidebar-content-site-profile-title">Dummy Locations</li>
{/* ITEMS */}
{
itemList.map((item, index) => (
<li
key={index}
className={selectedItem === index ? 'sidebar-content-dropdown-selected' : 'sidebar-content-dropdown'}
// onClick={() => setSelectedItem(index)}
>
{/* TITLE */}
<Link to="/" className="has-arrow dropdown-link">
{/* FOLDER ICON */}
<img
src={selectedItem === index ? IconFolderRed : IconFolderGray}
alt=''
className='bx sidebar-content-icon-folder-gray'
/>
{/* TEXT */}
<span className='dropdown-span'>
{item}
</span>
</Link>
{/* MENU COMPONENT*/}
<ul className="sub-menu" aria-expanded="false">
<div className='border-line'></div>
<MenuComponent list={menuList} pathName={item.toLowerCase()}/>
</ul>
</li>
))
}
{/* PROFILE */}
<li className='sidebar-content-single-item-container'>
<Link to='/'>
<div onClick={logoutUser}>
<img src={IconLogout} alt='' className='sidebar-content-icon-single-item'/>
<span className='sidebar-content-span-single-item'>Logout</span>
</div>
</Link>
</li>
</ul>
</div>
</SimpleBar>
</React.Fragment>
)
}
SidebarContent.propTypes = {
location: PropTypes.object,
t: PropTypes.any,
}
export default withRouter(withTranslation()(SidebarContent))
MenuComponent.js:
import React, { useState } from 'react'
import { Link } from "react-router-dom"
const MenuComponent = (props) => {
const [ selectedItem, setSelectedItem ] = useState(0)
return(
props['list'].map((item, index) => (
<li
key={index}
onClick={() => setSelectedItem(index)}
>
<Link
to={`${item['path']}/location=${props['pathName']}`}
className='menu-component-root'
>
{/* CIRCLE */}
<div className={selectedItem === index ? 'circle-selected' : 'circle'}></div>
{/* TEXT */}
<p className='text'>
{item['text']}
</p>
</Link>
</li>
))
)
}
export default MenuComponent
menuList.js:
const menuList = [
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/payload',
text: 'Payload'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
},
{
path: '/route/summary',
text: 'Route/Summary'
}
]
export default menuList
Summary.js:
import React from "react"
import { Row, Col } from "reactstrap"
// STYLES
import './styles.scss'
const Summary = () => {
return (
<React.Fragment>
<div className="container-fluid route-summary-root">
<Row>
<Col className="col-12">
{/* TOP CONTAINER */}
<div className='top-container'>
{/* LEFT CONTAINER */}
<div className='top-left-container'>
<h3 className='title'> Route/Summary Page</h3>
</div>
</div>
</Col>
</Row>
</div>
</React.Fragment>
)
}
export default Summary
Payload.js:
import React from "react"
import { Row, Col } from "reactstrap"
// STYLES
import './styles.scss'
const Payload = () => {
return (
<React.Fragment>
<div className="container-fluid payload-root">
<Row>
<Col className="col-12">
{/* TOP CONTAINER */}
<div className='top-container'>
{/* LEFT CONTAINER */}
<div className='top-left-container'>
<h3 className='title'>Payload Page</h3>
</div>
</div>
</Col>
</Row>
</div>
</React.Fragment>
)
}
export default Payload
Note: let me know if the question is confusing. I will try to update it.
this main App function has a custom hook that will trigger when the button is cliked:
import React, { useEffect } from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom'
import HomeScreen from './Screens/HomeScreen'
import './App.css';
import { useCurrentLang } from './utils/useCurrentLang'
import {strings as engstrings} from './res/lang/eng/strings'
function App() {
const currentStrings = useCurrentLang(engstrings);
return (
<BrowserRouter>
<div className="grid-container">
<header className="header">
<div className="brand">
<Link to="/" >
</Link>
</div>
<div className="header-side">
{currentStrings.currentlang.subtitle}
</div>
<div className="header-right">
<button {...currentStrings}>
{currentStrings.currentlang.traduction}
</button>
</div>
<div>
</div>
</header>
<main className="main">
<div className="content">
<Route path="/" exact={true} component={HomeScreen} />
</div>
</main>
<footer className="footer">
© 2020
</footer>
</div>
</BrowserRouter>
);
}
export default App;
However this component function that is routed from App also needs to use the same reference of the hook object found in App:
import React from 'react';
import terminalImage from '../res/images/GNOMETerminalIcon.png';
import {useCurrentLang} from '../utils/useCurrentLang'
import {strings as engstrings} from '../res/lang/eng/strings'
const { Link } = require("react-router-dom");
function HomeScreen() {
const currentStrings = useCurrentLang(engstrings);
return <div className="home">
<ul className="menu-list">
<li>
<div className="about-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
<li>
<div className="projects-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
<li>
<div className="contacts-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
<li>
<div className="suggestions-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
</ul>
<div className="home-main-image">
<img src={terminalImage} />
</div>
</div>
}
export default HomeScreen;
Is it possible for both function to rerender when the hook on App is triggered? if yes, how?
Edit: currentLang hook:
import {useState} from 'react'
import {strings as frstrings} from '../res/lang/fr/strings'
import {strings as engstrings} from '../res/lang/eng/strings'
export const useCurrentLang = initialState => {
if(initialState === 0){
initialState = engstrings
}
const [currentlang, setLang] = useState(initialState);
return {
currentlang: currentlang,
onClick: () => {
if(currentlang === engstrings){
setLang(frstrings)
} else {
setLang(engstrings)
}
}
}
}
Problem
The main problem you're facing is one part of your React Tree has no idea that the language has changed.
Solution
Use a Language Context which provides the updates to all your React tree and wrap it on the top of the application. In layman terms, now your app is on listening mode whenever lang changes. So basically, what React does now is whenever lang changes, it will find wherever lang is used from the context and update the component.
Docs on React context here
import React from 'react'
// import {strings as frstrings} from '../res/lang/fr/strings'
// import {strings as engstrings} from '../res/lang/eng/strings'
const lang = {
// in this way, you could dynamically add lang
// later on which worrying about if-elses in your component
en: {
hello: 'hello'
},
fr: {
hello: 'bonjour',
},
}
const langDict = (key) => lang[key]
const LanguageContext = React.createContext(null);
function LanguageProvider({ initialState = 'en', children }) {
const [lang, setLang] = React.useState(initialState);
return (
<LanguageContext.Provider value={[langDict(lang), setLang]}>
{children}
</LanguageContext.Provider>
)
}
function useLanguage() {
return React.useContext(LanguageContext);
}
export default function AppWrapper() {
return (
<LanguageProvider>
<App />
</LanguageProvider>
)
}
function App() {
const [lang, setLang] = useLanguage();
return (
<div>
<h1>{lang.hello}</h1>
<button onClick={() => setLang('fr')}>French</button>
<button onClick={() => setLang('en')}>English</button>
</div>
)
}