I integrated the template Core UI in my application.
The redirection is configured on _nav.js like presented by that picture:
I'm asking if it's possible to hide or display a menu depending on such condition ?.
For Exemple: Show Public Student and Hide Manage Convention depending on a condition.
The menu is defined on _nav.js
export default [
{
_tag: 'CSidebarNavTitle',
_children: ['Menu'],
},
{
_tag: 'CSidebarNavItem',
name: 'Public Space',
to: '/home',
}, // ...
{
_tag: 'CSidebarNavItem',
name: 'Manage Convention',
to: '/manageConvention',
} // ...
]
Then, this Array is called on TheSidebar.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { CCreateElement, CSidebar, CSidebarBrand, CSidebarNav, CSidebarNavDivider, CSidebarNavTitle, CSidebarMinimizer, CSidebarNavDropdown, CSidebarNavItem } from '#coreui/react'
import CIcon from '#coreui/icons-react'
// sidebar nav config
import navigation from './_nav'
const TheSidebar = () => {
const dispatch = useDispatch()
const show = useSelector(state => state.sidebarShow)
return (
<CSidebar
show={show}
onShowChange={(val) => dispatch({type: 'set', sidebarShow: val })}
>
<CSidebarBrand className="d-md-down-none" to="/">
<CIcon
className="c-sidebar-brand-full"
name="logo-negative"
height={35}
/>
<CIcon
className="c-sidebar-brand-minimized"
name="sygnet"
height={35}
/>
</CSidebarBrand>
<CSidebarNav>
<CCreateElement
items={navigation}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle
}}
/>
</CSidebarNav>
<CSidebarMinimizer className="c-d-md-down-none"/>
</CSidebar>
)
}
export default React.memo(TheSidebar)
Any suggestion will be appreciated.Big Thanks.
I know it's an old question but:
I think the simplest solution would be to add a meta prop inside _nav.js to every "CNavItem"
//_nav.js
const _nav = [
{
component: CNavItem,
name: 'Locations',
to: '/location',
meta: { role: ['Admin', 'Recruiter'] },
icon: <CIcon icon={cilLocationPin} customClassName="nav-icon" />
},
]
Helper function
export default function hasAccess(userRole, roles) {
if (Array.isArray(userRole)) {
return roles.some((r) => userRole.map((item) => item.toLowerCase()).includes(r.toLowerCase()))
} else {
return roles.map((item) => item.toLowerCase()).includes(userRole.toLowerCase())
}
}
After, inside AppSidebarNav.js component, check if the nav item includes at least one of the user role. If it does, render it, otherwise no.
userRole it's returned from redux store in this case. It's up to you how you get the logged in user roles.
//AppSidebarNav.js
export const AppSidebarNav = ({ items }) => {
const userRole = useSelector((state) => state.user.role)
{...}
const navItem = (item, index) => {
const { component, name, badge, icon, meta, ...rest } = item
const Component = component
return (
hasAccess(userRole, item.meta?.role || []) && <Component
{...(rest.to &&
!rest.items && {
component: NavLink,
})}
key={index}
{...rest}
>
{navLink(name, icon, badge)}
</Component>
)
}
{...}
return (
<React.Fragment>
{items &&
items.map((item, index) => (item.items ? (navGroup(item, index)) : navItem(item, index)))}
</React.Fragment>
)
}
You can use a child component class SidebarChild in TheSidebar.js to precise the condition that you want.
Then, inject it in <CSidebar >.
and don't forget to use the constant const SBChild = connect()(SidebarChild) to be able to call SBChild inside <CSidebar >.
Hope To Help.
Reading your comment, you can call an API to check for authorization (preferrably, just after login) and save it to state. You can use it as a flag as I've done below:
Try Changing the true on first line below to false.
const getWorldAccess = () => true;//Call API for user access rights here and save it to a state
const World = () => <div>World</div>;
const Hello = () => {
return (
<div>Hello {getWorldAccess() && <World />}</div>
);//replace getWorldAccess() with a state variable (you don't want to call API infinitely)
}
ReactDOM.render( <
Hello / > ,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You can filter the _nav.js array within the TheSideBar.js according to the condition you want. That will hide it from displaying in sidebar. You will have to know what the index of the item you want to remove.
I think this is the best answer to your question.
First, you have to divide items in the _nav.js file according to the below structure. (This allows, to export the navbar items according to user levels).
import React from 'react'
import CIcon from '#coreui/icons-react'
var navStaff = {
items: [
{
_tag: 'CSidebarNavItem',
name: 'Dashboard',
to: '/dashboard',
icon: <CIcon name="cil-speedometer" customClasses="c-sidebar-nav-icon"/>,
}
]
};
var navAdmin = {
items: [
{
_tag: 'CSidebarNavItem',
name: 'Dashboard',
to: '/dashboard',
icon: <CIcon name="cil-speedometer" customClasses="c-sidebar-nav-icon"/>,
},
{
_tag: 'CSidebarNavTitle',
_children: ['USERS']
},
{
_tag: 'CSidebarNavItem',
name: 'Users',
to: '/users',
icon: 'cil-people',
}
]
};
export {navStaff, navAdmin };
Then your TheSidebar.js should look like this which is used to allow different content in the navbar. If you want you can use the local storage to allow only the logged user type to allocate the selected navbar.
import React, { lazy, useState, useEffect, useRef} from 'react'
import { useSelector, useDispatch } from 'react-redux'
import {
CCreateElement,
CSidebar,
CSidebarBrand,
CSidebarNav,
CSidebarNavDivider,
CSidebarNavTitle,
CNavItem,
CProgress,
CSidebarMinimizer,
CSidebarNavDropdown,
CSidebarNavItem,
} from '#coreui/react'
import CIcon from '#coreui/icons-react'
// sidebar nav config
import {navStaff, navAdmin} from './_nav'
const TheSidebar = () => {
console.log(navStaff)
const dispatch = useDispatch()
const show = useSelector(state => state.sidebarShow);
return (
<CSidebar
show={show}
unfoldable
onShowChange={(val) => dispatch({type: 'set', sidebarShow: val })}
>
<CSidebarBrand className="d-md-down-none" to="/">
<CIcon
className="c-sidebar-brand-minimized"
name="sygnet"
height={35}
/>
</CSidebarBrand>
<CSidebarNav>
<CCreateElement
items={navAdmin.items}
components={{
CSidebarNavDivider,
CSidebarNavDropdown,
CSidebarNavItem,
CSidebarNavTitle
}}
/>
<CSidebarNavDivider />
</CSidebarNav>
<CSidebarMinimizer className="c-d-md-down-none"/>
</CSidebar>
)
}
export default React.memo(TheSidebar)
Hope you get the answer which you need. Thanks.
In the component, CSidebar just set value of minimize to true
Related
I'm trying to figure out how to set up a nextjs index.tsx page, that renders a page if the user is authenticated and another component if the user is not authenticated.
I can have the not authenticated component rendered properly, but I cannot have the authenticated page rendered correctly. I cant find a tutorial to explain how to put a page in the if statement so that the main nextjs index.tsx page renders the page I specify if there is an authenticated user.
I have an index.tsx in pages with:
import * as React from "react"
import { Box, Center, Spinner, VStack } from "#chakra-ui/react"
import Head from "next/head"
// import NextLink from "next/link"
import { useMe } from "lib/hooks/useMe"
import { DashLayout } from "components/DashLayout"
import { AuthedHomeLayout } from "components/AuthedHomeLayout"
import LandingPage from "components/landing/lp"
import { HomeLayout } from "components/HomeLayout"
export default function Home() {
const { me, loading } = useMe()
if (loading)
return (
<Center>
<Spinner />
</Center>
)
return (
<Box>
<Head>
<title>test</title>
</Head>
<Center flexDir="column" w="100%">
<VStack>
{me? <AuthedHomeLayout><DashLayout /></AuthedHomeLayout> : (
<HomeLayout><LandingPage /></HomeLayout>
)}
</VStack>
</Center>
</Box>
)
}
When I try this as an authenticated user, the DashLayout does load, but the links in it do not render.
The DashLayout has a set of links in it that form the pages of the dashboard:
import * as React from "react"
import { Box, Flex, Heading, Link, LinkProps, Stack, useColorModeValue } from "#chakra-ui/react"
import NextLink from "next/link"
import { useRouter } from "next/router"
const DashLayout: React.FC = ({ children }) => {
return (
<Box pt={10} pb={20} w="100%">
<Flex flexWrap={{ base: "wrap", md: "unset" }}>
<Box pos="relative">
<Stack
position="sticky"
top="100px"
minW={{ base: "unset", md: "200px" }}
mr={8}
flexDir={{ base: "row", md: "column" }}
mb={{ base: 8, md: 0 }}
spacing={{ base: 0, md: 4 }}
>
<ProfileLink href="/dash">Dashboard</ProfileLink>
<ProfileLink href="/dash/library">Library</ProfileLink>
<ProfileLink href="/dash/help">Help</ProfileLink>
</Stack>
</Box>
<Box w="100%">{children}</Box>
</Flex>
</Box>
)
}
export default DashLayout
interface ProfileLinkProps extends LinkProps {
href: string
}
const ProfileLink: React.FC<ProfileLinkProps> = ({ href, ...props }) => {
const { asPath } = useRouter()
const isActive = asPath === href
const activeColor = useColorModeValue("black", "white")
const inactiveColor = useColorModeValue("gray.600", "gray.500")
return (
<NextLink href={href} passHref>
<Link
pr={4}
h="25px"
justifyContent={{ base: "center", md: "flex-start" }}
textDecoration="none !important"
color={isActive ? activeColor : inactiveColor}
_hover={{ color: useColorModeValue("black", "white") }}
fontWeight={isActive ? "semibold" : "normal"}
>
{props.children}
</Link>
</NextLink>
)
}
The page I want to render if there is an auth user, is:
import * as React from "react"
import { gql } from "#apollo/client"
import { Center, Spinner, Stack, Text } from "#chakra-ui/react"
import { useUpdateMeMutation } from "lib/graphql"
import { useForm } from "lib/hooks/useForm"
import { useMe } from "lib/hooks/useMe"
import { useMutationHandler } from "lib/hooks/useMutationHandler"
import { UPLOAD_PATHS } from "lib/uploadPaths"
import Yup from "lib/yup"
import { ButtonGroup } from "components/ButtonGroup"
import { Form } from "components/Form"
import { withAuth } from "components/hoc/withAuth"
import { AuthedHomeLayout } from "components/AuthedHomeLayout"
import { ImageUploader } from "components/ImageUploader"
import { Input } from "components/Input"
import { DashLayout } from "components/DashLayout"
const _ = gql`
mutation UpdateMe($data: UpdateUserInput!) {
updateMe(data: $data) {
...Me
}
}
`
const ProfileSchema = Yup.object().shape({
email: Yup.string().email().required("Required").nullIfEmpty(),
firstName: Yup.string().required("Required").nullIfEmpty(),
lastName: Yup.string().required("Required").nullIfEmpty(),
})
function Dash() {
const { me, loading } = useMe()
const handler = useMutationHandler()
const [updateUser] = useUpdateMeMutation()
const updateAvatar = (avatar: string | null) => {
return handler(() => updateUser({ variables: { data: { avatar } } }), {
onSuccess: (_, toast) => toast({ description: "Avatar updated." }),
})
}
const defaultValues = {
email: me?.email || "",
firstName: me?.firstName || "",
lastName: me?.lastName || "",
}
const form = useForm({ defaultValues, schema: ProfileSchema })
const handleUpdate = (data: typeof defaultValues) => {
return form.handler(() => updateUser({ variables: { data } }), {
onSuccess: (_, toast) => {
toast({ description: "Info updated!" })
form.reset(data)
},
})
}
if (loading)
return (
<Center>
<Spinner />
</Center>
)
if (!me) return null
return (
<Stack spacing={6}>
<Tile>
<Text>alskjf</Text>
</Tile>
</Stack>
)
}
Dash.getLayout = (page: React.ReactNode) => (
<AuthedHomeLayout>
<DashLayout>{page}</DashLayout>
</AuthedHomeLayout>
)
export default withAuth(Dash)
I also tried defining the index.tsx condition as:
{me?
<Dash /> // Dash is defined as a page in the pages folder at dash/index
///<AuthedHomeLayout><DashLayout /></AuthedHomeLayout>
: (
<HomeLayout><LandingPage /></HomeLayout>
)}
How can I have index.tsx defined to render one page if there is an authed user and another if there is not?
I saw this post and tried using one of the suggestions it makes, as follows:
import Router from 'next/router';
{me? Router.push('/dash') : (
<HomeLayout><LandingPage /></HomeLayout>
)}
When I try this, I get errors that read:
[{
"resource": "/src/pages/index.tsx",
"owner": "typescript",
"code": "2322",
"severity": 8,
"message": "Type 'Element | Promise<boolean>' is not assignable to type 'ReactNode'.\n Type 'Promise<boolean>' is not assignable to type 'ReactNode'.",
"source": "ts",
"startLineNumber": 32,
"startColumn": 13,
"endLineNumber": 34,
"endColumn": 15,
"relatedInformation": [
{
"startLineNumber": 1360,
"startColumn": 9,
"endLineNumber": 1360,
"endColumn": 17,
"message": "The expected type comes from property 'children' which is declared here on type 'IntrinsicAttributes & OmitCommonProps<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof StackProps> & StackProps & { ...; }'",
"resource": "repo/node_modules/#types/react/index.d.ts"
}
]
}]
In the solutions you tried, the last one was almost correct.
You were on the right path, that you should redirect the user to the /dash page if he is authenticated. But you were doing the redirection in the return statement of your component, which is not where you want to do any side effect logic.
Your attempt:
import Router from 'next/router';
{me? Router.push('/dash') : (
<HomeLayout><LandingPage /></HomeLayout>
)}
will not work because Router.push returns a <Promise<boolean>>.
Don't forget that React components must return React elements. In your case when the user is authenticated, you are returning a promise not a React element.
So your redirection (which is a side effect) should be done inside a useEffect hook.
In order to fix this, Next documentation provides a clear example of how to do it correctly. What you are looking for is the last code block of this section (the one just before this section).
Don't forget to use a valid router instance via the useRouter hook provided by next/router.
So your code now becomes something like:
import { useEffect } from 'react';
import { useRouter } from 'next/router';
// Whatever Component you were doing the redirect
const export YourComponent = () => {
// your component hooks and states
const { me, loading } = useMe();
const router = useRouter();
// Here is what you were missing
useEffect(() => {
if (me) {
router.push('/dash');
}
}, [me]);
// you can add a loader like you did before
return loading ? (
<Center><Spinner /></Center>
) : (
<HomeLayout><LandingPage /></HomeLayout>
);
};
It should be enough to get to what you're looking for.
As a side note, your first solution:
{me?
<Dash /> // Dash is defined as a page in the pages folder at dash/index
///<AuthedHomeLayout><DashLayout /></AuthedHomeLayout>
: (
<HomeLayout><LandingPage /></HomeLayout>
)}
cannot work, as <Dash /> is a Next Page which is associated with a route based on its file name. You can look at it like an entry point.
I am learning React with Typescript. I have a view like below.
index.tsx
import React from 'react'
import { CButton } from '#coreui/react'
import { useHistory } from 'react-router'
const Team = () => {
const history = useHistory()
const handleNewMember = () => {
history.push('/team/add-member')
}
return (
<React.Fragment>
<div className="page-title">
<CButton color="primary" className="long-btn" onClick={handleNewMember}>
Add a New Member
</CButton>
</div>
</React.Fragment>
)
}
export default React.memo(Team)
I have below code in route.ts file
const AddMember = React.lazy(() => import('./views/team/add-member'))
const routes = [
{ path: '/team/add-member', exact: true, name: 'Add Team Member', component: AddMember },
]
export default routes
When I click on the button I can see URL http://localhost:3000/#/team/add-member at address bar of browser. But I can't see the View (HTML).
What is the solution ?
Because handleNewMember is an async function.
I'm building a pretty simple restaurant website using React and Next.js. I have a home page and an 'order' page, which renders a menu from a state object ('menuitems'). I want the user to be able to add items to their 'cart', which is another state object. So ultimately I'm transferring data from the static 'menuitems' state to the 'cart.'
What I can't figure out is how I can update the 'cart' state from my 'order' page.
I've set up a context for the app going off of this guide. I've been able to successfully access the menu using a call to the custom hook 'useAppContext()' but I no longer have access to the updater functions provided through useState() or useContext() calls I previously used when I built something similar with everything in a single file (you can see in my code below where I invoke the now-undefined setCartItem() function).
How can I update the 'cartitems' state from inside of my 'order' component?
File where I have my context object:
import { createContext, useContext } from "react";
const AppContext = createContext();
export function AppWrapper({ children }) {
const state = {
menuitems: [
{
title: "Spagett",
description: "Our finest spagett made with homemade meatballs and sauce.",
},
{
title: "Sandwich",
description: "You gotta try this sandwich",
},
{
title: "BFB",
description: "Watch out for your toilet after this one bro",
},
],
cartitems: []
}
return (
<AppContext.Provider
value={state}
>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
My _app.js file:
import "../styles/globals.css";
import { AppWrapper } from "./context/state";
function MyApp({ Component, pageProps }) {
return (
<AppWrapper>
<Component {...pageProps} />
</AppWrapper>
);
}
export default MyApp;
Finally, my 'order' component, where I am trying to update the 'cartitems' state:
import Link from "next/link";
import { useAppContext } from "./context/state";
//IMPORT CONTEXT
const OrderPage = () => {
const { menuitems } = useAppContext();
const { cartitems } = useAppContext();
const renderedMenu = menuitems.map((item) => (
<div key={item.name} className="order-item">
<h4>{item.title}</h4>
<p>{item.description}</p>
<button onClick={() => setCartItem([...cartitems, item.title])}>
Add Item
</button>
</div>
));
return (
<div>
<Link href="/">
<a>Go home</a>
</Link>
<div>{renderedMenu}</div>
</div>
);
};
export default OrderPage;
Create a state in your provider, then pass setCartItems into your context.
export function AppWrapper({ children }) {
const [ cartItems, setCardItems ] = useState([])
const state = {
menuitems: [
{
title: "Spagett",
description: "Our finest spagett made with homemade meatballs and sauce.",
},
{
title: "Sandwich",
description: "You gotta try this sandwich",
},
{
title: "BFB",
description: "Watch out for your toilet after this one bro",
},
],
cartitems: []
}
return (
<AppContext.Provider
value={{ state, cartItems, setCardItems }}
>
{children}
</AppContext.Provider>
);
}
You can then use it this way.
const { state, cartItems, setCartitems } = useAppContext();
I ran into some issues with my Gatsby web application when trying to implement a store for global states with Redux. I am new to both. Before I worked with MobX and "plain" React.
The problem is, that I cannot access the data of my store from my components. I use the Redux Provider class as I read in several tutorials, but as I make use of other providers as well, my case seems to be special... This is what I came up with so far:
gatsby-ssr.js and gatsby-browser.js
import prepPages from "./prepPages"
export const wrapRootElement = prepPages
prepPages.js
import React from "react"
import { createGlobalStyle, ThemeProvider } from "styled-components"
import { Provider } from "react-redux"
import createStore from "./src/state/store"
import { MDXProvider } from "#mdx-js/react"
import { Table } from "./src/components"
import Theme from "./src/themes/theme"
//Provider for my global styling
const GlobalStyles = createGlobalStyle`...`
//Overriding the table component
const components = {
table: Table
}
export default ({ element }) => {
const store = createStore()
return(
<Provider store={store}>
<MDXProvider components={components}>
<ThemeProvider theme={Theme}>
<GlobalStyles/>
{element}
</ThemeProvider>
</MDXProvider>
</Provider>
)
}
store.js
import {createStore as reduxCreateStore} from "redux"
const initialState = {
loggedIn: false,
menuToggleOn: false,
//other initial states
}
const reducer = (state, action, dispatch) => {
//Toggles
if(action.type === 'TOGGLE_MENU'){
return {
...state,
toggleMenuOn: !state.toggleMenuOn
}
}
//other actions
}
const createStore = () => reduxCreateStore(reducer, initialState);
export default createStore;
components/Nav.js
import React from 'react';
import {useStaticQuery, Link, graphql} from "gatsby";
import {NavWrapper} from "../styles";
import { Button } from "./Button";
import {FontAwesomeIcon} from "#fortawesome/react-fontawesome";
import {faBars} from "#fortawesome/free-solid-svg-icons";
import connect from "react-redux/lib/connect/connect";
import PropTypes from "prop-types"
const NavHolder = ({loggedIn, toggleMenu, toggleMenuOn}) => {
...
//defining the nav items depending on the login state
var items;
var cta = {};
if(loggedIn){
items = [
{name: "home", ref: "/", key: 0},
{name: "wiki", ref: "/wiki", key: 1},
{name: "workspace", ref: "/workspace", key: 2}
]
}else {
items = [
{name: "about", ref: "#about", key: 0},
{name: "features", ref: "#features", key: 1},
{name: "download", ref: "#download", key: 2},
{name: "contact", ref: "#contact", key: 3}
];
cta = {exists: true, name: "login"}
}
//mapping the nav items and adding the visible class if the menu is toggled on
let navItems = items.map((item) =>
<li key={item.key} className={toggleMenuOn ? "nav-item visible" : "nav-item"}>
<a href={item.ref} className={isActive ? "active":""}>
{item.name}
</a>
</li>
)
return (
<NavWrapper>
<ul>
<li>
<Link to="/" id="logo">...</Link>
</li>
{navItems}
<li className="nav-toggle">
<a href="/" onClick={toggleMenu}>
<FontAwesomeIcon icon={faBars}/>
</a>
</li>
{cta.exists && (
<li className="nav-cta">
<Button color="petrol" href="login">{cta.name}</Button>
</li>
)}
</ul>
</NavWrapper>
)
}
NavHolder.propTypes = {
loggedIn: PropTypes.bool.isRequired,
menuToggleOn: PropTypes.bool.isRequired,
toggleMenu: PropTypes.func.isRequired
}
const mapStateToProps = ({loggedIn, toggleMenuOn}) => {
return { loggedIn, toggleMenuOn }
}
const mapDispatchToProps = dispatch => {
return { toggleMenu: () => dispatch({ type: `TOGGLE_MENU` }) }
}
const ConnectedNav = connect(mapStateToProps, mapDispatchToProps)(NavHolder)
export const Nav = () => {
return <ConnectedNav/>
}
I thought this might work, but I get this error:
Error: Could not find "store" in the context of "Connect(NavHolder)". Either wrap the root component in a Provider, or pass a custom React context provider to Provider and the corresponding React context consumer to Connect(NavHolder) in connect options.
could not find store in component error
Has anyone an idea where I went wrong? I am really grateful for any help.
Thanks :)
With Gatsby you need to use wrapRootElement API.
Wrap the root element in your Gatsby markup once using wrapRootElement, an API supporting both Gatsby’s server rendering and browser JavaScript processes.
Refer to Adding a Redux Store in Gatsby docs, there is an example repo for that.
I'm creating the redux state in this page :
import React from 'react';
import { connect } from 'react-redux';
import styled from 'styled-components';
import wrapper from '../redux/store';
import Container from '../components/Container/Container';
import Card from '../components/Card/Card';
import Circle from '../components/Circle/Circle';
import PieChart from '../components/PieChart/PieChart';
import Accordion from '../components/Accordion/Accordion';
import RadioButton from '../components/Ui/RadioButton/RadioButton';
import { manageList, reportList } from '../components/helper';
import { getManageListAndCategoryId } from '../redux/actions/actions';
const Panel = ({ manageProductsList }) => (
<>
{console.log(manageProductsList)}
<MainContainer>
<Title>Управление</Title>
<ContainersWrapper>
{manageProductsList.map((item, index) => <Card key={index} title={item.title} type="service" serviceName={item.value} />)}
</ContainersWrapper>
<SecondSection>
<CustomContainer>
<Title>Отчетность</Title>
<p>Показатели за:</p>
Здесь будут ТАБЫ
<ContainersWrapper>
{reportList.map((item, index) => <Card key={index} item={item} type="report" />)}
</ContainersWrapper>
<DiagreammWrapper>
<PieChart />
<Circle percent={20} />
<Circle percent={87} />
<Circle percent={30} />
<Circle percent={47} />
</DiagreammWrapper>
</CustomContainer>
</SecondSection>
<CustomContainer>
<TitleTwo>Доступные отчеты</TitleTwo>
<Accordion />
<RadioButton />
</CustomContainer>
</MainContainer>
</>
);
export const getStaticProps = wrapper.getStaticProps(async ({ store }) => {
store.dispatch(getManageListAndCategoryId(manageList));
});
const mapStateToProps = (state) => ({
manageProductsList: state.mainReducer.manageProductsList,
});
export default connect(mapStateToProps, null)(Panel);
And I still can see the data manageProductsList (screenshot) in Redux in this page. But when I navigate to another dynamic route page forms/[id.tsx]
import React from 'react';
import { connect } from 'react-redux';
import wrapper from '../redux/store';
import { util, manageList, reportList } from '../../components/helper';
import { getManageListAndCategoryId } from '../../redux/actions/actions';
export async function getStaticPaths(categoryIds) {
console.log('categoryIds', categoryIds);
//temporarely make static path data while categoryIds is undefined
const paths = [
{ params: { id: 'object' } },
{ params: { id: 'service' } },
{ params: { id: 'club_cards' } },
{ params: { id: 'schedule' } },
{ params: { id: 'agents' } },
{ params: { id: 'abonements' } },
{ params: { id: 'price_category' } },
{ params: { id: 'person_data' } },
{ params: { id: 'roles' } },
];
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params, manageProductsList }) {
// const postData = util.findFormData(params.id, manageProductsList);
const postData = { title: 'asdsadasdsad' };
return {
props: {
postData,
},
};
}
const Form = ({ manageProductsList }) => (
<div>
{console.log(manageProductsList)}
{/* {postData.title} */}
dasdsadsad
</div>
);
const mapStateToProps = (state) => ({
categoryIds: state.mainReducer.categoryIds,
manageProductsList: state.mainReducer.manageProductsList,
});
export default connect(mapStateToProps, null)(Form);
the manageProductsList and categoryIds are empty arrays (screenshot 2)
I am using native Link from next/link component to navigate the page
Here is Card component which navigate to dynamic page:
import React, { FunctionComponent, HTMLAttributes } from 'react';
import styled from 'styled-components';
import Link from 'next/link';
import EditIcon from '#material-ui/icons/Edit';
import AddIcon from '#material-ui/icons/Add';
interface CardProps extends HTMLAttributes<HTMLOrSVGElement>{
title: string
type: string
item?: {
title: string
amount: number
}
serviceName: string
}
const Card: FunctionComponent<CardProps> = ({
type, title, serviceName, item,
}) => (
<>
{
type === 'service'
&& (
<FirstSection>
<h1>{title}</h1>
<ImageWrapper>
<Link href={`/forms/${serviceName}`}>
<a><AddIcon fontSize="large" onClick={(e) => { console.log(serviceName); }} /></a>
</Link>
<EditIcon />
</ImageWrapper>
</FirstSection>
)
}
{
type === 'report'
&& (
<SecondSection>
<h1>{item.title}</h1>
<p>{item.amount}</p>
</SecondSection>
)
}
</>
);
export default Card;
I would be very gratefull if someone can help
Your <Link> will cause server-side rendering, you can observe whether the browser tab is loading or not when navigate to another page. If it is, the page will reload and the redux state would be refresh.
The official docs shows the right way for using dynamic route.
<Link href="/forms/[id]" as={`/forms/${serviceName}`}>