For my application, I have a nav bar which has different functionality for different pages. ie. 1 page has a map and the search will search for addresses and another page has a list and the search bar will filter the list. There are also different buttons on each page too.
What's the best way to implement something like this in react? I've thought about creating a different search bar component for every page and just rendering a different one for each route but I don't have enough experience in React to go ahead with that decision. Is there a more efficient alternative?
Create a new Navbar component named something like NavBarController. While calling the navbar controller component pass in the "Type" as prop. Type should be a state and should change depending on the page the user is on.
<NavBarController type={1}></NavBarController>
You would just create a few different NavBars and then NavBarController will handle whichever navbar you want to display out of your multiple navbars.
Your NavBarController will return something like this:
return (props.type===1?<NavBarHome/>
:props.type===2?<NavBarMap/>
:props.type===3?<NavBarList/>)
Related
I would love to implement the following behavior:
User comes to a screen at /list URL that stores its state in search params (e.g. a paginated list -> /list?page=2)
When the user clicks one of the list items, the URL changes to /list/1 and a modal opens. The list itself stays on page no. 2.
A modal itself stores some of its state in search params as well (e.g. /list/1?tab=first).
When the modal is closed, the user returns back to /list?page=2
I started with nested routes on a codesandbox, but I can't wrap my head around this.
What would be a good way to approach this?
From my understanding, React Router is good for when you want to reuse a container and different routes will change what's displayed inside of the container. However, I want it such that a route will change what's displayed in multiple containers.
For example, in my login page, I have my login form in the body container and my link to registration in the footer.
In my registration page, I have my registration form in the body and the link to login in the footer.
In my home page, I have a welcome message in the body and some buttons to change tabs in the footer.
What's the best practice for the component structure involved in this?
the best practice of React is making reused-components that help not to waste your time in coding, just only pick and reuse components in many different scenarios.
Base on your example, in login page and registration the footer may have the same design but only the links is different. We can create a layout named LinkInFooter and receive an array link objects to displayed:
// file /pages/login.js
<PageLayout>
<LoginComponent/>
< LinkInFooter links={[url: '/registration', label: 'registration page']}/>
</PageLayout>
// file /pages/registration.js
<PageLayout>
<RegistrationComponent/>
<LinkInFooter links={[url: '/login', label: 'login page']}/>
</PageLayout>
Below is a few tips for you:
Should have a WrappedLayout for all pages, I don't say it should be the same layout for all pages but it should have the same layout for many pages. That WrappedLayout will make your page look consistent when change route or hide / show chilren components.
Layouts/*.js : Layout directory is the place that hold all your custom small components that you want they are displayed same across your projects. For example: I customized a Select component that allow to search item, select multiple items... So that I create an custom <Select/> layout to replace html's <select/> on my project.
Components/*.js: this one hold all your components, either reusable or non-reusable. A component can be made by compose many others components: what can be reuseable should be created a new file for it and import to use. If not, create an internal component is enough.
Pay 5 minutes on UI design before start. Use the initial time to imagine the structure of pages then answer these questions :
How many component should I have for this pages?
Could be reused frequently?
Which items on the design should be layout?
I have a question about React, here's a simplified version of a React app.
In the app I want to render a fixed primary menu and a secondary menu that is optional and its content is controlled by inner components rendered in routing.
Also secondary menu is rendered somewhere else in mobile version of the app.
function App() {
return <Router>
<PrimaryMenu/>
<SecondaryMenu/>
<LayoutContent/>
{/* This block is rendered only on mobile devices */}
<Responsive {...Responsive.onlyMobile}>
<SecondaryMenu/>
</Responsive>
</Router>;
}
LayoutContent will render actual page content (using a Page component) according to routing rules and every page component may render its own secondary menu like this (e.g. page1 has its own submenu, page2 has another one, page3 has not.)
<Page title='Page 1 - With secondary menu'>
<SecondaryMenuItems>
{/* I want this content as children of secondary menu in both mobile and desktop menubars */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenuItems>
</Page>
I tried to implement it by using React Contexts but if I store children nodes in context an infinite render is triggered. I changed it to use a id property in <SecondaryMenuItems/> component but the approach is very fragile and also has some drawbacks.
Here's my working example it's working but as I said is pretty fragile:
What if I use a duplicate id for secondary menus?
What if I forget a secondary menu key?
Also if you switch to a page with a menu and then go to page3 (that has no menu) previous page menu remain on screeen.
How to accomplish this with react? Is there a suggested way to do that?
A simpler way to express my question is "how to pass a set of react nodes between unrelated components (e.g. siblings components)"
Update
I've completed my working example with received hints, now by combining useRef with ReactDOM.createPortal I achieved final result which is now in the example.
This is a use case for React Portals. Portal will let you render secondary menu items from a page into secondary menu container that exists somewhere else
All you need to do is to call React.createPortal in render of thepage, pass rendered element and target node to render into, regardless of position in DOM tree
I've edited your example using portals here https://codesandbox.io/s/secondary-menu-example-vbm3x. This of course is a basic example, you might want to abstract portals logic in a separate component for convenience, and/or pass dom reference from parent, instead of calling getElementById on mount
Rendering same children in multiple sibling nodes
The question asks "how to pass a set of react nodes". Ideally, don't. If you are rendering nodes somewhere in your hierarchy with the intention of using them elsewhere, you may be using the wrong strategy.
If you need to render the same components in different places, make a function that renders the components, and call it from both places. In other words, always pass the information, not the rendered elements.
Render inside the router
In a typical Single Page Application, the router will render all of the (non-static) components. This is how the example should have done it. The routing component (LayoutContent) should have been responsible for rendering the "passed nodes" (SecondaryMenu) directly.
<Route path="/page1">
<Page title="Page 1 - With secondary menu">
<SecondaryMenu id="menu1"> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenu> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
</Page>
</Route>
When rendering inside the router is impossible
If for some reason the routing component cannot render the content directly, then a Single Page Application (or routing) solution is probably the wrong solution here. The question doesn't include any information as to why the components can't be rendered inside the router, feel free to edit the question and comment with more info.
Another way of achieving the example would be for there to be no routing component (i.e. no LayoutContent) and for SecondaryMenu to check the path of the page and conditionally render the appropriate content based on that.
It may seem silly to manually render conditionally based on a path when there is a router component which does this for you, and I would agree. The solution is then to not use a router at all. Trying to render children in the router and passing them has a strong code smell.
In the React hierarchical layout, if the same information is needed make decisions about rendering in multiple places (the path in this case), move that information up to the nearest parent of all components and pass it down as props or as context.
Avoiding ID clashes
"What if I use a duplicate id for secondary menus?"
If you call a function to render the secondary menu instead of rendering it and passing it, then you can pass a menu prefix in the props, and use this menu prefix in the function.
function SecondaryMenuItems({ children, idPrefix, path }) {
if (path == '/path1') {
return (
<ul id={`${idPrefix}-newlist`}>
On keys
"What if I forget a secondary menu key?"
React keys need only be unique within a rendered list. In fact, keys are simply an optimisation to prevent React having to re-render a generated list on every pass. If you forget to include a key (or make a bad choice of key), React has to re-render the list every time, but it's not more serious than that. In simple cases, you won't notice the drop in performance.
I am trying to implement the React Router in such a way that it supports a route like this:
/myPages/:pageName1/:pageName2/:pageName3/...
The idea is that, even though the page being rendered is only the last page, the pages are nested, and the depth is not something that is pre-determined. The component that renders the actual page needs to know the names of parent pages.
To clarify, the idea is that the page data in the backend are in a tree structure in such a way that, in the above example, page1 is the root page, page 2 is a child of page1, page 3 is a child of page2, etc. In addition, one page can have multiple children. The last child name (so pageName3 in the example) is what is being used to query the database and get all the content required to render the full page. The parent names are required to render navigation-related subcomponent. I should also mention that just having /myPages/:pageName3 and getting all parent names from the backend is not really an option. I could fetch that information from the backend, but the URL presented to the user still needs to have that structure.
I am hoping that there's a way to get this type of information as an array, but I am having a hard time finding an example like this on the web.
maybe this can help.
https://github.com/ReactTraining/react-router/blob/d28d46dce08a5756a085f7e5eebb5169ea59e40b/packages/react-router/docs/api/Redirect.md#from-string
states:
A pathname to redirect from. Any valid URL path that path-to-regexp#^1.7.0 understands.
maybe you can combine
<Redirect from='/users/:id' to='/users/profile/:id'/>
with
var re = pathToRegexp('/:foo+', keys)
(taken from https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#one-or-more)
then you'll end up with
<Redirect from='/:pageName+/:id' to='/:id'/>
I am using admin-on-rest in my React app and wonder how is possible to make a custom page containing Show (or another detail component) with specified resource Id (for example: I want my app to fetch only resource with id = 1)
I don't know if this is canonical or the intended way to do things but you can supply both basePath and record as props to the ShowButton and EditButton components.
You can use this to redirect your user basically anywhere.
It should be possible (using some switch case or dependent input addon) to also add these props to the ListButton in the Show and Edit view and redirect your user back to the originating ListView.
Here is documentation to supply actions to the List view.
https://marmelab.com/admin-on-rest/List.html#actions