Use TypeScript in React - reactjs

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.

Related

EditorJS is not showing in NextJS even it is loaded through SSR:false

so I am integrating EditorJs with the NextJs app I have done the initialization in the console it shows Editojs is ready but on the screen, it is not visible
can anyone please tell me what I am doing wrong I am sharing my code below
Editor.js
import { createReactEditorJS } from 'react-editor-js'
import { EditorTools } from './EditorTools';
import React, { useEffect } from 'react'
const Editor = () => {
const ReactEditorJS = createReactEditorJS();
return (
<div>
<ReactEditorJS holder="customEditor" tools={EditorTools}>
<div id="customEditor" />
</ReactEditorJS>
</div>
)
}
export default Editor
EditorTools.js
import Header from '#editorjs/header';
export const EditorTools = {
header: {
class: Header,
config: {
placeholder: 'Let`s write an awesome story! ✨',
},
},
};
Create.js
import React from 'react'
import dynamic from 'next/dynamic';
const EditorJSNoSSRWrapper = dynamic(import('../../../components/Editor/Editor'), {
ssr: false,
loading: () => <p>Loading ...</p>,
});
const create = () => {
return (
<div>
<EditorJSNoSSRWrapper />
</div>
)
}
export default create

React Router (V6) Link works without a server request but useNavigate doesn't

I've been using 'react-router-dom' (v6) in combination with Microsoft Fluint UI Nav.
Because of this I have to programmatically navigate instead of using the <Link></Link> element.
When I use a <Link to="test">to test</Link> it navigates to the requested page without a server request. But when I use this (basically an eventHandler that calls navigate for the redirect) :
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
const navLinkGroups: INavLinkGroup[] = [
{
links: [{ name: 'test', url: 'test', onClick: () => navigate('test') }],
},
];
return (
<Stack><Nav groups={navLinkGroups} isOnTop={true} /></Stack>
);
It does a server request when going to the 'test' page. Is this by design or am I missing something?
UPDATE
You can reproduce it this way:
Create the project: npx create-react-app my-app --template #fluentui/cra-template
Edit App.tsx like:
import { BrowserRouter, Route, Routes } from 'react-router-dom';
//UI-Components import
import { Header } from './UI-Components/Header';
// Page Import
import { TestPage } from './Pages/Test';
function App() {
return (
<BrowserRouter>
<Header />
<Routes>
<Route path="test" element={<TestPage />} />
</Routes>
</BrowserRouter>
);
}
export default App;
Create Headers.tsx (UI-Components folder)
Add Header.tsx content:
import { Stack } from '#fluentui/react/lib/Stack';
import { Nav, INavLinkGroup, INavProps } from '#fluentui/react/lib/Nav';
import { useNavigate } from 'react-router-dom';
import { Link } from 'react-router-dom';
export const Header = () => {
const navigate = useNavigate();
const navLinkGroups: INavLinkGroup[] = [
{
links: [{ name: 'test', url: 'test', onClick: () => navigate('test') }],
},
];
return (
<Stack>
<Stack><Nav groups={navLinkGroups} isOnTop={true} /></Stack>
<Stack></Stack>
<Link to="test">to test</Link>
<Stack></Stack>
<Stack></Stack>
</Stack>
);
};
Create Test.tsx in the Pages folder
Add content export const TestPage = () => <div>test</div>;
Now you should be able to reproduce the problem
Noob mistake...
I forgot to preventDefault(). Fixed it like:
function handleNavClick(event?: React.MouseEvent<HTMLElement>, element?: INavLink) {
if (event && element) {
event.preventDefault();
navigate(element.url);
}
}
const navLinkGroups: INavLinkGroup[] = [
{
links: [{ name: 'test', url: 'test', onClick: (event, element) => handleNavClick(event, element) }],
},
];

Hide and Display menu Core UI

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

Could not find "store" in the context of "Connect(Component)" GatsbyJs Redux

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.

React - frontend component test with Jest

I've just written test file for my component, at the moment it's very rudimentary.. I'm quite inexperience in written test for frontend. I ran yarn test to this test file and it failed miserably..
Here is the message:
Unable to find an element with the text: Please review your billing details...
This is what I have so far for my test:
import React from 'react';
import { render, cleanup, waitForElement } from 'react-testing-library';
// React Router
import { MemoryRouter, Route } from "react-router";
import Show from './Show';
test('it shows the offer', async () => {
const { getByText } = render(
<MemoryRouter initialEntries={['/booking-requests/20-A1-C2/offer']}>
<Route
path="/booking-requests/:booking_request/offer"
render={props => (
<Show {...props} />
)}
/>
</MemoryRouter>
);
//displays the review prompt
await waitForElement(() => getByText('Please review your billing details, contract preview and Additions for your space. Once you’re happy, accept your offer'));
//displays the confirm button
await waitForElement(() => getByText('Confirm'));
});
and this is the component:
// #flow
import * as React from 'react';
import i18n from 'utils/i18n/i18n';
import { Btn } from '#appearhere/bloom';
import css from './Show.css';
import StepContainer from 'components/Layout/DynamicStepContainer/DynamicStepContainer';
const t = i18n.withPrefix('client.apps.offers.show');
const confirmOfferSteps = [
{
title: t('title'),
breadcrumb: t('breadcrumb'),
},
{
title: i18n.t('client.apps.offers.billing_information.title'),
breadcrumb: i18n.t('client.apps.offers.billing_information.breadcrumb'),
},
{
title: i18n.t('client.apps.offers.confirm_pay.title'),
breadcrumb: i18n.t('client.apps.offers.confirm_pay.breadcrumb'),
},
];
class Show extends React.Component<Props> {
steps = confirmOfferSteps;
renderCtaButton = (): React.Element<'Btn'> => {
const cta = t('cta');
return <Btn className={css.button} context='primary'>
{cta}
</Btn>
};
renderLeftContent = ({ isMobile }: { isMobile: boolean }): React.Element<'div'> => (
<div>
<p>{t('blurb')}</p>
{!isMobile && this.renderCtaButton()}
</div>
);
renderRightContent = () => {
return <div>Right content</div>;
};
render() {
const ctaButton = this.renderCtaButton();
return (
<StepContainer
steps={this.steps}
currentStep={1}
ctaButton={ctaButton}
leftContent={this.renderLeftContent}
rightContent={this.renderRightContent}
footer={ctaButton}
/>
);
}
}
export default Show;
what am I missing? Suggestions what else to add to my test file would be greatly appreciated!

Resources