Updated component based on state from other component - javascript

I have a hamburger component that has no className prop, only color, and using the ternary operator its color changes only after page reload, but for example FontAwesomeIcon using dark:text-gray-50 changes its color.
This is my TodoHeader.jsx component
import Search from "./Search";
import Switcher from "../Switcher";
import {faBars} from "#fortawesome/free-solid-svg-icons";
import {FontAwesomeIcon} from "#fortawesome/react-fontawesome";
import {Fade as Hamburger} from "hamburger-react";
import {useProSidebar} from "react-pro-sidebar";
import useDarkSide from "../../hooks/useDarkSide";
const TodoHeader = () => {
const {collapseSidebar} = useProSidebar();
const [colorTheme, setTheme] = useDarkSide();
const toggle = () => {
collapseSidebar()
}
return (
<div className={"flex items-center justify-between border-b-2 dark:border-gray-600"}>
<div className={"flex items-center ml-8"}>
<Hamburger direction={"right"} onToggle={toggle} color={ colorTheme === 'light' ? "#f3f4f6" : "#1f2937"}/>
<FontAwesomeIcon icon={faBars} onClick={toggle} className={"text-center dark:text-gray-50"}/>
<h1 className={"p-4 text-2xl text-gray-800 dark:text-gray-100"}>Todo List</h1>
</div>
<Search/>
<Switcher/>
</div>
);
};
export default TodoHeader;
This is my customhook
import {useEffect, useMemo, useState} from "react";
const useDarkSide = () => {
const [theme, setTheme] = useState(localStorage.theme);
const colorTheme = theme === "dark" ? "light" : "dark";
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove(colorTheme);
root.classList.add(theme);
localStorage.setItem('theme', theme);
}, [theme, colorTheme]);
return [colorTheme, setTheme]
}
export default useDarkSide
And this is Switcher.jsx component that handles dark/light mode changes
import {useState} from "react";
import { DarkModeSwitch} from "react-toggle-dark-mode";
import useDarkSide from "../hooks/useDarkSide";
const Switcher = () => {
const [colorTheme, setTheme] = useDarkSide();
const [darkSide, setDarkSide] = useState(
colorTheme === "light" ? true : false
);
const toggleDarkMode = (checked) => {
setTheme(colorTheme);
setDarkSide(checked);
};
return (
<>
<DarkModeSwitch
style={{ marginRight: '2rem' }}
checked={darkSide}
onChange={toggleDarkMode}
size={30}
/>
</>
);
}
export default Switcher
I think the problem is that when I use the useDarkSide hook on the header it doesn't render the header component, but I don't know how to make it work

So, the problem was with me trying to access theme state that was inside switcher component through useDarkSide custom hook, but it's create another separated state. So the solution was using global state, in my case i used redux.

Related

Importing a variable from dynamically imported component

I have a (Map) component dynamically imported to my page.
const Map = dynamic(() => import('../components/Mapcomp'), { ssr: false, })
From that component I also want to import a variable.
const [taskImg, setTaskImg] = useState(null);
export {taskImg};
The component is dynamically imported because otherwise I get a window is not defined error, and this is the solution for that.
Same with the variable. If I want to import it I get the above error.
But I need to use the taskImg variable in this page.
How can I do that?
Mapcomp:
import { MapContainer, TileLayer, useMap, Marker, Popup,
ImageOverlay } from 'react-leaflet'
import { useEffect, useState } from "react";
import Head from "next/head"
import { LatLngBounds } from 'leaflet';
const Map = () => {
const [userPos, setUserPos] = useState(null)
const [taskPos, setTaskPos] = useState(null)
const [task, setTask] = useState()
const [taskImg, setTaskImg] = useState(null);
const bounds = new LatLngBounds([81.505, -0.09], [50.773941, -84.12544])
useEffect(() => {
function getRandomTask(arr) {
const randomIndex = Math.floor(Math.random() * arr.length);
const item = arr[randomIndex];
return item;
}
const tasks = [
"boller",
"placeholder"
];
setTask(getRandomTask(tasks));
},[])
useEffect(() => {
if(task == "boller"){
console.log("boller")
setTaskImg("/sf2.jpg")
} else if(task == "placeholder") {
console.log("placeholder")
}
},[task])
return (
<>
<Head>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""></script>
</Head>
<MapContainer
className='absolute h-[750px] w-[750px] left-[45%] top-[80px] bg-no-repeat bg-cover bg-[#738aaf]'
center={[71.505, -40.09]} zoom={3} scrollWheelZoom={true} noWrap={true}>
<ImageOverlay
url="/allmaphres.png"
bounds={bounds}
opacity={1}
zIndex={10}
/>
</MapContainer>
</>
);
}
export default Map;
page (mapcomp is rendered here):
import { memo, useEffect, useMemo, useState } from "react";
import dynamic from 'next/dynamic'
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import Head from "next/head";
const Map = dynamic(() => import('../components/Mapcomp'), {
ssr: false,
})
const All = () => {
return (
<>
<Navbar />
<div className="bg-white w-[500px] h-[100vh]">
<div className="task border-4 border-red-500 h-[250px]">
{/* taskImg && <img src={taskImg}/> */}
</div>
<div className="flex justify-center gap-[200px] text-xl">
<span>Tasks:</span>
<span>Points:</span>
</div>
<div className="flex justify-center gap-[240px] text-xl">
<span>X</span>
<span>X</span>
</div>
</div>
<Map />
<Footer />
</>
);
}
export default All;
You can't export a state to another component you either need to use a state management library like redux or create a context which make the state available for all the other components

ReactJS/MUI (v5.6.2) Dark mode toggle not updating state

I am trying to wrap my mind around this and find the breaking point for all morning but am not able to. I have only followed the official docs until now.
I need to update the theme from dark to light in a React App but the state wont change (or resets immediately)
Heres my code:
App.tsx
import React, { createContext, useMemo, useState } from 'react';
import styles from './App.module.scss';
import {createTheme,CssBaseline,PaletteMode,responsiveFontSizes,ThemeProvider,
useMediaQuery} from '#mui/material';
import { getDefaultThemeMode } from './shared/theme/default-theme-mode';
import { getDesignTokens } from './shared/theme/mui-design-tokens';
import Main from './main/Main';
import { Navigate, Route, Routes } from 'react-router-dom';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { OPTIONS } from './shared/i18n/translations';
export const ColorModeContext = React.createContext({
toggleColorMode: () => { },
});
function App() {
// THEME
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [mode, setMode] = React.useState<'light' | 'dark'>('light');
React.useEffect(() => {
setMode(prefersDarkMode ? 'dark' : 'light');
}, [prefersDarkMode]);
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
},
}),
[]
);
let theme = React.useMemo(
() =>
createTheme(getDesignTokens(mode)),
[mode]
);
theme = responsiveFontSizes(theme);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<div className={styles.app} style={{background: theme.palette.background.default}}>
<CssBaseline/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<Routes>
<Route path='/home' element={<Main/>}/>
<Route path='*' element={<Navigate to='home'/>}/>
</Routes>
</div>
</ThemeProvider>
</ColorModeContext.Provider>
);
}
export default App;
getDesignTokens Function:
const getDesignTokens = (mode: PaletteMode): ThemeOptions => ({
palette: {
mode,
...(mode === 'light' ? {
primary: {
main: '#3c3c3c',
},
} : {
primary: {
main: '#3c3c3c',
},
})
}});
And finally, I am trying to update the theme from outside the App.tsx component this way:
const ThemeToggle = () => {
const theme = useTheme();
const colorMode = useContext(ColorModeContext);
return (
<>
<label htmlFor='theme-toggle' className={`${styles.toggleBtn}
${theme.palette.mode === 'dark' ? styles.dark : ''}`}
onClick={colorMode.toggleColorMode}>
<input type='checkbox' id='theme-toggle' className={styles.input}/>
</label>
</>
)
}
export default ThemeToggle;
I must be missing something obvious but have tried everything. Anyone has done something similar?

useContext() second argument is reserved for future use in React. Passing it is not supported

I have a lot of globally declared states in my application. I used useContext for implementing it, however, I keep getting a bunch of errors in the console regarding useContext.
Error:
Warning: useContext() second argument is reserved for future use in React. Passing it is not supported. You passed: false.
in Navbar (at Home.js:11)
in Home (created by Context.Consumer)
in Route (at App.js:96)
in Switch (at App.js:76)
in Router (created by BrowserRouter)
in BrowserRouter (at App.js:74)
in App (at src/index.js:7)
in StrictMode (at src/index.js:6)
App.js:
import React, { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import './App.css';
import Home from '../pages/Home';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Restaurants from '../pages/Restaurants';
import ScrollToTop from '../components/ScrollToTop';
import UserAccount from '../pages/UserAccount'
import Footer from '../components/footer/Footer';
import { UserContext } from '../UserContext';
function App() {
const [clickedDistrict, setClickedDistrict] = useState(false);
const [clickedSuggestion, setClickedSuggestion] = useState(false);
const [checkedDistance, setCheckedDistance] = useState("1000")
const [restaurants, setRestaurants] = useState([]);
const [clickedUserMenuItem, setClickedUserMenuItem]
= useState("saved")
const [goodPassword, setGoodPassword] = useState(false)
const [chosenRestaurant, setChosenRestaurant] = useState(false);
const [generalSearchPath, setGeneralSearchPath] = useState(false);
const [incorrectPassword, setIncorrectPassword] = useState(false);
const [successfullLogin, setSuccessfullLogin] = useState(false)
const [username, setUsername] = useState(false)
const [incorrectOldPassword, setIncorrectOldPassword] = useState(false)
const [logout, setLogout] = useState(false);
const [newUsername, setNewUsername] = useState(false)
const [incorrectPasswordOnDelete, setIncorrectPasswordOnDelete] = useState(false)
const [deleteAccount, setDeleteAccount ] = useState(false)
return (
<>
<Router>
<ScrollToTop />
<Switch>
<UserContext.Provider value={{
clickedDistrict, setClickedDistrict,
clickedSuggestion, setClickedSuggestion,
checkedDistance, setCheckedDistance,
restaurants, setRestaurants,
clickedUserMenuItem, setClickedUserMenuItem,
goodPassword, setGoodPassword,
chosenRestaurant, setChosenRestaurant,
generalSearchPath, setGeneralSearchPath,
incorrectPassword, setIncorrectPassword,
successfullLogin, setSuccessfullLogin,
username, setUsername,
incorrectOldPassword, setIncorrectOldPassword,
logout, setLogout,
newUsername, setNewUsername,
incorrectPasswordOnDelete, setIncorrectPasswordOnDelete,
deleteAccount, setDeleteAccount
}}>
<Route path='/' exact component={Home} />
<Route path='/restaurants' component={Restaurants} />
{ (successfullLogin && !logout && !deleteAccount) ?
<Route path='/user' component={UserAccount} />
:
<>
<Route path='/user' component={UserAccount} />
<Redirect to='/' /></>
}
</UserContext.Provider>
</Switch>
<Footer />
</Router>
</>
);
}
export default App;
UserContext.js:
import { createContext } from "react";
export const UserContext = createContext();
I tried searching about this warning but nothing helped. I don't even understand the reason for this error. Which second argument does it imply?
Also, the weird thing is that I have theese states used throughout the application, but the error shows that I have a problem in the navbar on the Home page.
Home:
import React from 'react';
import Cards from '../components/cards/Cards';
import HeroSection from '../components/hero/HeroSection';
import Districts from '../components/districts/Districts';
import Navbar from '../components/navbar/Navbar';
import CollegeSection from '../components/college/CollegeSection';
function Home() {
return (
<>
<Navbar />
<HeroSection />
<Cards />
<CollegeSection />
<Districts />
</>
);
}
export default Home;
import React, { useEffect, useContext } from 'react';
import { Button } from '../button/Button';
import MobileNavbar from './MobileNavbar'
import { Link } from 'react-router-dom';
import './Navbar.css';
import { Modal } from '../forms/Modal'
import Searchbox from '../search/Searchbox';
import NavbarLogic from './NavbarLogic';
import { UserContext } from '../../UserContext';
import UserNavbar from '../user/UserNavbar';
function Navbar() {
const { click, button, showButton,
handleClick, closeMenuDiscardChanges, closeMenuOpenPCRestaurants }
= MobileNavbar();
const { navMenuClassName, searchbox, showLogInModal,
showSignUpModal, openLogInModal, openSignUpModal,
setShowLogInModal, setShowSignUpModal }
= NavbarLogic();
const { successfullLogin, setSuccessfullLogin, logout }
= useContext(UserContext);
useEffect(() => {
showButton();
}, [showButton]);
useEffect(() => {
if (logout) {
setShowLogInModal(false);
setSuccessfullLogin(false)
}
}, [logout,setSuccessfullLogin,setShowLogInModal])
window.addEventListener('resize', showButton);
return (
<>
<nav className={click ? 'navbar active' : 'navbar'}>
<div className='navbar-container'>
<Link to='/' className='navbar-logo' onClick={closeMenuDiscardChanges}>
Restaurateur<i class="fas fa-utensils" />
</Link>
<div className={click ? 'hidden' : searchbox}>
<Searchbox />
</div>
<div className='menu-icon' onClick={handleClick}>
<i className={click ? 'fas fa-times' : 'fas fa-bars'} />
</div>
<ul className={click ? 'nav-menu active' : navMenuClassName}>
<li className='nav-item'>
<Link to='/' className='nav-links' onClick={closeMenuDiscardChanges}>
Home
</Link>
</li>
<li className='nav-item'>
<Link
to='/restaurants'
className='nav-links'
onClick={setRestaurantsNavLink() === "All Restaurants" ?
closeMenuDiscardChanges
:
closeMenuOpenPCRestaurants}
>
{setRestaurantsNavLink()}
</Link>
</li>
<li>
<Link
className='nav-links-mobile'
onClick={openLogInModal}
>
Log In
</Link>
<Link
className='nav-links-mobile'
onClick={openSignUpModal}
>
Sign Up
</Link>
</li>
</ul>
{successfullLogin === true ?
<UserNavbar />
:
<>
{button &&
<Button
buttonStyle='btn--outline'
buttonSize='btn--medium'
onClick={openLogInModal}
id="login">
LOG IN
</Button>}
<Modal
showLogInModal={showLogInModal}
setShowLogInModal={setShowLogInModal}
/>
{button &&
<Button
id="signup"
buttonSize='btn--medium'
onClick={openSignUpModal}>
SIGN UP
</Button>}
<Modal
showSignUpModal={showSignUpModal}
setShowSignUpModal={setShowSignUpModal}
/>
</>}
</div>
</nav>
</>
);
}
export default Navbar;
How can I get rid of this useContext warning?
Edit:
This is how I use useContext in Mobilenavbar:
import { useState, useContext } from 'react';
import { UserContext } from "../../UserContext"
const MobileNavbar = () => {
const [click, setClick] = useState(false);
const [button, setButton] = useState(true);
const { setPragueCollegePath } = useContext(UserContext)
const { setClickedDistrict, setClickedSuggestion } = useContext(UserContext)
const { setChosenRestaurant, setGeneralSearchPath } = useContext(UserContext);
const handleClick = () => setClick(!click);
const closeMenuOpenRestaurants = () => {
setClick(false);
setClickedDistrict(false);
setClickedSuggestion(false);
setChosenRestaurant(false);
setGeneralSearchPath(false)
}
const closeMenuOpenPCRestaurants = () => {
setClick(false);
setClickedDistrict(false);
setClickedSuggestion(false);
setChosenRestaurant(false);
setGeneralSearchPath(false);
}
const closeMenuDiscardChanges = () => {
setClick(false);
setClickedDistrict(false)
setClickedSuggestion(false)
setChosenRestaurant(false)
setGeneralSearchPath(false);
}
const showButton = () => {
if (window.innerWidth <= 1120) {
setButton(false);
} else {
setButton(true);
}
};
const showSearch = () => {
if (window.innerWidth <= 820) {
setButton(false);
} else {
setButton(true);
}
};
return {
click, button, showButton, handleClick,
showSearch, closeMenuOpenRestaurants,
closeMenuOpenPCRestaurants, closeMenuDiscardChanges
}
}
export default MobileNavbar;
And here is NavbarLogic:
const {successfullLogin, setSuccessfullLogin, logout }
= useContext(UserContext);
I usually import all the variables together.

How to open/close antd Submenu programatically

I have two components, ParentComponent and a child component called SelectionTool. The child component SelectionTool have and SubMenu item which is disabled and not collapsed by default. Disabled SubMenu can be enabled using a prop. What I'm trying to do is, I need to collapse/expand the SubMenu once it's enabled via the prop. Here is my code. I tried using defaultOpenKeys attribute in the ParentComponent Menu item. but it's not working. Any kind of help is highly appreciated. Thank you.
Parent component
import { useState } from 'react'
import { Layout, Menu} from 'antd'
import SelectionTool from '../selection-tool/SelectionTool'
const {Sider} = Layout
const ParentComponent = props => {
const [defaultOpenKeys, setDefaultOpenKeys] = useState([])
return (
<Sider
width={240}
defaultCollapsed={false}
collapsedWidth={80}
theme="light" trigger={null} collapsible>
<Menu theme="light" mode="inline" defaultOpenKeys={defaultOpenKeys}>
<SelectionTool setDefaultOpenKeys={setDefaultOpenKeys} />
</Menu>
</Sider>
)
}
export default ParentComponent
Child Component
import { useEffect, useState } from 'react'
import { Menu } from 'antd'
const { SubMenu } = Menu
const SelectionTool = (props) => {
const [isDisabled, setIsDisabled] = useState(true)
const { setDefaultOpenKeys, selectionToolIsDisable, ...submenuProps } = props
useEffect(() => {
setIsDisabled(selectionToolIsDisable)
}, [selectionToolIsDisable])
useEffect(() => {
isDisabled && setDefaultOpenKeys([submenuProps.eventKey])
}, [isDisabled])
return(
<SubMenu
title="Selection tools"
deisabled={isDisabled}
key={submenuProps.eventKey}
{...submenuProps}>
<Menu.Item>Menu item 1</Menu.Item>
<Menu.Item>Menu item 2</Menu.Item>
</SubMenu>
)
}
export default SelectionTool
I was able to find a solution myself. This solved my issue:-
import { useState, useEffect } from 'react'
import { Layout, Menu } from 'antd'
import SelectionTool from '../selection-tool/SelectionTool'
const { Sider } = Layout
const ParentComponent = props => {
const [openKeys, setOpenKeys] = useState([])
useEffect(() => {
if (selectionToolIsDisable) {
const openedKeyIndex = openKeys.indexOf('menu1')
if (openedKeyIndex !== -1) {
const _openKeys = [...openKeys]
_openKeys.splice(openedKeyIndex, 1)
setOpenKeys(_openKeys)
}
} else {
setOpenKeys([...openKeys, 'menu1'])
}
}, [selectionToolIsDisable])
const onOpenChange = _openKeys => {
setOpenKeys([..._openKeys])
}
return (
<Sider
width={240}
defaultCollapsed={false}
collapsedWidth={80}
theme="light" trigger={null} collapsible>
<Menu
theme="light"
mode="inline"
openKeys={openKeys}
onOpenChange={onOpenChange}
key="menu1">
<SelectionTool selectionToolIsDisable={selectionToolIsDisable} />
</Menu>
</Sider>
)
}
export default ParentComponent

reactjs createRef not work in component arrays

like this i hava a array of components need ref to trigger the comment component collapse, so i need to create some refs to reference each commentListItem, but it doesn't work, how do i do this work?
import React, { useRef, createRef } from "react";
import PropTypes from "prop-types";
import { map, isArray } from "lodash/fp";
import Divider from "#material-ui/core/Divider";
import CommentListItem from "./CommentListItem";
import CommentCollapse from "./CommentCollapse";
function CommentList({ list = [], ...props }) {
const { count = 0 } = props;
const refList = map((o) => {
/* o.ref = createRef(null); */
return o;
})(list);
const onShow = () => {
console.log(refList);
};
return (
<div className="ke-comment-list">
{map.convert({ cap: false })((o, i) => (
<div key={i} className="ke-comment-list-item">
<CommentListItem listItem={o} onShow={onShow} />
{isArray(o.child) && o.child.length ? (
<CommentCollapse {...o}>
<CommentList list={o.child} count={count + 1} />
</CommentCollapse>
) : null}
{count > 0 && list.length - 1 === i ? null : <Divider />}
</div>
))(refList)}
</div>
);
}
CommentList.propTypes = {
list: PropTypes.arrayOf(PropTypes.object).isRequired,
};
export default CommentList;
there is CommentCollapse component for show or hide subcomment.
import React, { useState, forwardRef, useImperativeHandle } from "react";
import ButtonBase from "#material-ui/core/ButtonBase";
import Collapse from "#material-ui/core/Collapse";
const CommentCollapse = ({ children }, ref) => {
const [show, setShow] = useState(false);
const showMore = () => {
setShow((prev) => !prev);
};
const collapseText = () => (show ? "收起" : "展开");
useImperativeHandle(ref, () => ({
showMore: showMore()
}));
return (
<div className="ke-comment-list-children">
<Collapse in={show}>{children}</Collapse>
<ButtonBase size="small" onClick={showMore}>
{collapseText()}
</ButtonBase>
</div>
);
};
export default forwardRef(CommentCollapse);
catch errors
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
have any idear for this situation?
is fixed, just not trigger showMore function in ref.
import React, { useState, forwardRef, useImperativeHandle } from "react";
import ButtonBase from "#material-ui/core/ButtonBase";
import Collapse from "#material-ui/core/Collapse";
const CommentCollapse = ({ children }, ref) => {
const [show, setShow] = useState(false);
const showMore = () => {
setShow((prev) => !prev);
};
const collapseText = () => (show ? "收起" : "展开");
useImperativeHandle(ref, () => ({
showMore
}));
return (
<div className="ke-comment-list-children">
<Collapse in={show}>{children}</Collapse>
<ButtonBase size="small" onClick={showMore}>
{collapseText()}
</ButtonBase>
</div>
);
};
export default forwardRef(CommentCollapse);

Categories