Setting up a working URL with React Router - javascript

I'm having some real trouble setting up a react router and would really appreciate some suggestions. I have an app with multiple pages, and i want the app to display the page i'm on in url and be able to go to specific page through url. But i just cant seem to get it working.
The route path is:
<Route path="/page/{:id}" component={Body} />
It's calling Body, which is my main Class component where most of the code and logic is. But most of the threads and tutorials i've seen use either useEffect or useParams, which can't be done in a class component.
The app is simple, all it does is change between pages and calls api on every page to get different text and button numbers.
So what i'm doing now is i have a Link tag wrapped around my buttons with page numbers <Link key={index} to={`/page/${links.label}`}>. Those buttons are in a seperate function component. And i see the change in url based on which page i'm on. But i want to set it up so i can type the url directly.
buttons:
const renderPageNumbers = apiPagingSliced.map((links, index ) => {
return <Link key={index} to={`/page/${links.label}`}>
<button key={index} id={links.label}
onClick={props.handleClick}
>{links.label}
</button>
</Link>
})
and it just seems very incomplete. I tried using useParams, but couldn't get it to work.

Add this into your Body Component so when id param change your page get update and update your localStorage too
componentDidUpdate(prevProps, prevState, snapshot){
if(prevProps){
if(prevProps.match.params.id !== this.props.match.params.id){
const id = this.props.match.params.id;
localStorage.setItem("currentPage", id)
this.setState ({
currentPage: id});
this.fetchApi(id);
}
}
}

Related

How to set menu item to active in sveltekit app

I have been working on a sveltekit application and recently ran into an issue with my main navigation menu. when the application is loaded or refreshed the current menu item that corresponds with the current URL is not set to active. (i.e. menu item 'Home' would have URL of '/') I had a hard time finding anything useful online demonstrating how to set this up, but I have a working solution and thought I'd document it for others. It is very simple at the moment.
I have a nav.json file where I define any menus within my application, making it easy to organize menus. I import that at the top of the file and store it in an array for use later.
import menuObj from '../nav/nav.json';
let sidebar = menuObj.menus[0].items;
onMount() is just getting the current path when the nav component is loaded, and only runs once for me.
then - using <svelte:window on:click={handlePageClick}/> we can listen to any clicks on the page.
In the handlePageClick(e) method we pass the on:click event. Using that event we can capture the current pathname of the URL. this allows us to check the pathname against our item.path which is defined in the nav.json file I've imported and extracted using the #each method in our html. On the <a> we can set the class to active if the items route matches the current path.
here is the full implementation
<script>
import menuObj from '../nav/nav.json';
import {onMount} from "svelte";
let path;
let sidebar = menuObj.menus[0].items;
onMount(() => {
path = window.location.pathname;
})
function handlePageClick(e) {
path = e.view.location.pathname;
}
</script>
<svelte:window on:click={handlePageClick}/>
<nav class="sidebar">
{#each sidebar as item, i}
<a href={item.route} id="sidebar-menu-item-{item.id}"
class={path === item.route ? "item active" : "item"}
>
<i id="sidebar-menu-item-{item.id}-i" class="{item.icon}"></i>
</a>
{/each}
</nav>
I haven't found any drawbacks to this yet. I will update this question if I find anything.
It's not really a question, but I will add an answer anyway because your method is not necessary, SvelteKit already has a built in system for this:
<script>
import { page } from '$app/stores';
</script>
<a href={item.route} class="item" class:active={$page.url.pathname == item.route}>
...
</a>
page is a store like object that among other things hold the current path, it will automatically be updated with the new path
as a bonus:
class:active is an easy shorthand, removing that ternary expression there.

React - How to apply multiple values inside style

I have a certain component named ThemeDoc inside this component there is a context, localStorage, hooks, maps and whatnot, but in the end I get the value of the hooks inside a component named SideBar. I decided that there is no need to demonstrate the contents of the ThemeDoc component in order not to complicate my code and question, but I will attach a link of my project on the github if you want you can see GitHub Project. So as I just mentioned, I get the value of the hooks from the ThemeDoc component for the SideBar component, they look like this
const {SideBarValue, SideBarWallpaperValue, SideBarColor} = React.useContext(CounterContext);
const [SideBarTheme, SetSideBarTheme] = SideBarValue;
const [SideBarBackground] = SideBarWallpaperValue;
const [SideBarColorTheme, SetSideBarColor] = SideBarColor;
These values are used to change the color of the SideBar.
<div style={{background: SideBarTheme}}>
...
</div>
It all looks like this
Please see there are two values for SideBarTheme and SideBarColorTheme, SideBarTheme changes the background of the site to a gradient and the SideBarColorTheme only changes the SideBar color. As you can see I successfully applied the SideBarTheme value inside jsx and everything works fine for me
<div style={{background: SideBarTheme}}>
...
</div>
But I can’t use SideBarColorTheme, I don’t know how to do it, here is my full-fledged code if you need to tell me I will provide the ThemeDoc component
SideBar.jsx
function SideBar(props) {
const {someValue} = useContext(SideBarContext);
const {SideBarValue, SideBarColor} = React.useContext(CounterContext);
const [SideBarTheme, SetSideBarTheme] = SideBarValue;
const [SideBarColorTheme, SetSideBarColor] = SideBarColor;
return (
<div style={{background: SideBarTheme, backdropFilter: `blur(${someValue}px)`}}>
...
</div>
);
}

How to preserve children state when changing parents in react?

I am new to React and working on an app that changes navbar layout and some other details according to screen size, but renders the same responsive content inside. The <MyContent /> itself can vary according to the route or tab the user is visiting.
function App() {
const isMobile = useSomeHookToDetectIfMobile();
return (
{isMobile
? <MobileWrapper><MyContent /></MobileWrapper>
: <DesktopWrapper><MyContent /></DesktopWrapper>
}
);
}
The thing is, if the user resizes the window, when changing layout all the state inside <MyContent /> is lost and reset. What would be the most React way to maintain it on resize? Keep in mind that the shape of the state of <MyContent /> can vary according to what components it is displaying, so I don't think lifting state up is the way to go. Also its state only concerns itself, so I don't think it belongs farther up the component chain. I think I am missing some key point about the problem or coming at it the wrong way. Any help would be appreciated.
In this case, a simple local variable to keep the rendered content before the condition would be enough. That way, React can keep track of it and then it's always the same instance.
function App() {
const isMobile = useSomeHookToDetectIfMobile();
const content = <MyContent />;
return (
{isMobile
? <MobileWrapper>{content}</MobileWrapper>
: <DesktopWrapper>{content}</DesktopWrapper>
}
);
}

How to remove UI components using React JS

I'm having trouble finding relevant documentation on how to remove UI components when using react. For example, there's a login form. The user clicks submit and now the form should be removed from the screen. How do I do this?
I've found unmountComponentAtNode, but that can only be invoked at the parent level. Am I supposed to have a parent node that is aware of all child state and loads them conditionally? Should all children have an "isHidden" attribute which renders the dom as hidden if true?
This must be basic but I don't see this in the react js tutorials. I found this stackoverflow post (react.js: removing a component) is this really the pattern? It kind of makes sense but it means that a large app will likely have an extremely complex Application parent class that manages maps of application state based on configuration.
It seems like i need to start defining application state as named maps. For example:
BaseApp: showHeader=true;showContent=true;
LoginState: showBaseApp=true;showLoginForm=true;
LoggedInState: showBaseApp=true;showFeed=true;
At any moment we would have to update all state maps and call the base class render method...
In my opinion your question isn't about removing component but about showing the right component. And yes - it can be done with a component state but with Flux/Redux store/reducer as well.
In your example with a login form after click on "Submit" we can change local state for the component and show another text like "The request was sent blah-blah-blah" or another component.
But you can do this by extracting component's local state to a store/reducer and it'll be work better in relatively big app. Nevertheless, it's really up to you where you want to store state.
I like to use a hide prop like so.
class AppCtrlRender extends React.Component {
render() {
let page = this.state.appState.currentPage;
let hideAbout = (page != 'about');
let hideHome = (page != 'home');
return (
<div id='AppCtrlSty' style={AppCtrlSty}>
<div id='allPageSty' style={allPageSty}>
<AboutPage hide={hideAbout} />
<HomePage hide={hideHome} />
</div>
</div>
);
}
}
export default class AboutPage extends React.Component {
render() {
if (this.props.hide) return null;
let aTime = (new Cache()).time.toString();
return (
<div style={AboutPageSty}>
React 0.14 ReFlux used for app state. This is the About Page.
<NavMenu /><br/><br/>
{aTime}
</div>
);
}
}

Show/Hide ReactJS components without losing their internal state?

I've been hiding/showing react components by not rendering them, for example:
render: function() {
var partial;
if (this.state.currentPage === 'home') {
partial = <Home />;
} else if (this.state.currentPage === 'bio') {
partial = <Bio />;
} else {
partial = <h1>Not found</h1>
}
return (
<div>
<div>I am a menu that stays here</div>
Home Bio
{partial}
</div>
);
}
but just say that the <Bio/> component has lots of internal state. Everytime I recreate the component, it loses it's internal state, and resets to it's original state.
I know of course that I could store the data for it somewhere, and pass it in via props or just globally access it, but this data doesn't really need to live outside of the component. I could also hide/show components using CSS (display:none), but I'd prefer to hide/show them as above.
What's the best practice here?
EDIT: Maybe a better way to state the problem is to use an example:
Ignore React, and assume you were just using a desktop app that had a configuration dialog with a Tab component called A, which has 2 tabs, named 1 and 2.
Say that tab A.1 has an email text field and you fill in your email address. Then you click on Tab A.2 for a second, then click back to Tab A.1. What's happened? Your email address wouldn't be there anymore, it would've been reset to nothing because the internal state wasn't stored anywhere.
Internalizing the state works as suggested in one of the answers below, but only for the component and it's immediate children. If you had components arbitrarily nested in other components, say Tabs in Tabs in Tabs, the only way for them to keep their internal state around is to either externalize it somewhere, or use the display:none approach which actually keeps all the child components around at all times.
It just seems to me that this type of data isn't data you want dirtying up your app state... or even want to even have to think about. It seems like data you should be able to control at a parent component level, and choose to either keep or discard, without using the display:none approach and without concerning yourself with details on how it's stored.
One option would be to move the conditional inside the component itself:
Bio = React.createClass({
render: function() {
if(this.props.show) {
return <p>bio comp</p>
} else {
return null;
}
}
});
<Bio show={isBioPage} />
Whether this is "best practise" or not probably depends on the exact situation.
Unfortunately, style={{display: 'none'}} trick only works on normal DOM element, not React component. I have to wrap component inside a div. So I don't have to cascade the state to subcomponent.
<div className="content">
<div className={this.state.curTab == 'securities' ? 'active' : ''}>
<Securities />
</div>
<div className={this.state.curTab == 'plugins' ? 'active' : ''}>
<Plugins />
</div>
</div>
Looks like official documentation suggests hiding stateful children with style={{display: 'none'}}
The fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?
I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale
Rationale
react/react-dom comes comes with 2 basic assumptions/ideas:
every UI is hierarchical naturally. This why we have the idea of components which wrap each other
react-dom mounts (physically) child component to its parent DOM node by default
The problem is that sometimes the second property isn't what you want
in your case. Sometimes you want to mount your component into
different physical DOM node and hold logical connection between
parent and child at the same time.
Canonical example is Tooltip-like component: at some point of
development process you could find that you need to add some
description for your UI element: it'll render in fixed layer and
should know its coordinates (which are that UI element coord or
mouse coords) and at the same time it needs information whether it
needs to be shown right now or not, its content and some context from
parent components. This example shows that sometimes logical hierarchy
isn't match with the physical DOM hierarchy.
Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question (take a look at the "use" property):
import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
return (
<Cell {...props}>
// the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
<Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
hideMe, // alias for `hide(modalId)`
index } // useful to know to set zIndex, for example
, e) => // access to the arguments (click event data in this example)
<Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
<ConfirmationDialog
title={ 'Delete' }
message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
confirmButton={ <Button type="primary">DELETE</Button> }
onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
close={ hideMe } />
</Modal> }
</Layer>
// this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
<LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
<div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
<Icon type="trash" />
</div> }
</LayerContext>
</Cell>)
// ...

Categories

Resources