How do you nest React components/modules to separate the UI? - javascript

I have three main components for a dashboard UI I'm working on (I'm a React beginner, coming from Angular): a sidebar, top nav, and a content container.
How would I split these into three separate UI components and call them in other components? I want to be able to do this:
<Sidenav /> <!-- sidenav component from Sidenav.js -->
<section className="content">
<Topnav /> <!-- top nav component from Topnav.js -->
<div className="wrapper container">
<!-- Content here -->
</div>
</section>
And also, how would you use the <div className="wrapper container"></div> as a view for all content?
I'm using ES6 and the React Starterify app kit.

This is how I would do it (you'll notice I name all my component files .jsx instead of .js, though it doesn't matter either way. I've even seen people do Component.jsx.js):
src/index.html
<html>
<head>
...
</head>
<body>
<script src="js/bundle.min.js"></script> <!-- I'm assuming you're using Browserify or similar to bundle the entire app into a single file -->
</body>
</html>
src/js/main.js
import React from 'react';
import {render} from 'react-dom';
import {Routes} from '../components';
render(Routes, document.body);
src/components/App.jsx
import React from 'react';
import Topnav from './Topnav';
module.exports = React.createClass({
displayName: 'App',
propTypes: {
children: React.PropTypes.shape({
props: React.PropTypes.object
})
},
render () {
return (
<section className="content">
<Topnav />
<div className="wrapper container">
{this.props.children}
</div>
</section>
);
}
});
{this.props.children} will render the component that is handling the current route.
src/components/Topnav.jsx
...
Think of it the same way you would create grouping in an object-oriented language like Java. Components that belong with each other should go together. So, for example, if I had to write a Profile component, that might look like:
-- src
-- components
-- profile
-- index.js // Used to export Profile
-- Profile.jsx // This would have the profile layout and be a parent to all other components in this folder
-- Address.jsx
-- Gravatar.jsx
-- ...

Related

How to open/close a modal from my header component in Next.js 13?

I am building an e-commerce application with Next.js 13 for fun. I have difficulties when I try to make a Login modal that pops up when I click on a button in <Header/> component.
My approach would be make a useState in the parent, which is the rootLayout here, pass the setter to <Header/>, and pass the state to the modal.
But my question is, since I will be using useState to toggle the visibility of the modal, I will need to use client component by declaring "use client". But if I use "use client" in my root layout, and use the useState hook here, will it make my whole app client component? If that's the case, then this is not ideal. How should I implement it? Is it a bad layout? Should I change it? Thanks.
This is my root layout:
import "./globals.css";
import Header from "./Header";
import LoginForm from "#components/LoginForm";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head />
<body className="relative">
<Header /> <-------------------------- THIS IS THE HEADER/NAVBAR COMPONENT
<main className="p-5">
<div>{children}</div>
</main>
<LoginForm /> <------------------------ THIS IS THE LOGIN MODAL COMPONENT
</body>
</html>
);
}

Vuejs: shared components used on multiple pages disappeared

right now, I'm writing a single-page-application in vue.js using vue-router. Pages like the homepage, sign-in page etc. all share a navigation and footer component. On a few pages however, I need the entire screen so that the navigation and footer shall not be displayed.
Hence, I decided to nest components and include the navigation and footer component when necessary. My problems now is, that the navigation and footer template disappeared on all pages.
Edit: A more complete demo can be found in this Github repository.
Here's a simplified version of the files I'm using:
index.html:
<div id="app">
<router-view></routerview>
</div>
router.js:
import Homepage from './homepage.vue';
import SignIn from './signin.vue';
const router = new VueRouter({
routes: [
{path: '/', component: Homepage},
{path: '/signin', component: SignIn},
]
})
homepage.vue and signin.vue components:
<template>
<navigation></navigation>
// some page-specific content
<footer-vue></footer-vue>
</template>
<script>
import Navigation from './navigation.vue';
import Footer from './footer.vue';
export default {
components: {
'navigation': Navigation,
'footer-vue': Footer,
},
}
</script>
A component without navigation and footer:
<template>
// some page-specific content
</template>
Is it even possible to nest components this way? I hope someone is able to point me into the right direction.
Both homepage.vue and signin.vue have invalid templates. e.g.
<template>
<navigation></navigation>
<h1>The homepage</h1>
<footer-vue></footer-vue>
</template>
This is not allowed as it has 3 root nodes. See https://v2.vuejs.org/v2/guide/components.html#A-Single-Root-Element
You need to wrap it to get it to work:
<template>
<div>
<navigation></navigation>
<h1>The homepage</h1>
<footer-vue></footer-vue>
</div>
</template>
Note that this limitation does not apply to functional components and is also expected to be lifted for all components in Vue 3.
Much more worrying is that you're not seeing any errors messages for this. You really need to look into that as it suggests there's something very much amiss with your development setup.
An example of the error message you should be seeing:
new Vue({
el: '#app',
template: '<div></div><div></div>'
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
</div>

Nuxt.js: How to use the same page with different URLs and switch the components to be displayed according to the URL

I want to implement the following in Nuxt.js:
1.Use the same page with different URLs.
Specifically, I want to use /pages/users/_userId.vue with/users/{userId}, /users/{userId}/follow and /users/{userId}/follower.
I examined this and there were the following issues.
- https://github.com/nuxt/nuxt.js/issues/2693
But it was a little different from what I wanted to achieve.
I want to use the pass parameter for the query parameter.
2.Identify the components to display by path parameters
It would be quicker to have a look at the code here.
・/pages/users/_userId.vue`
<template>
<div class="user">
<div class="user__contents">
<div class="user__contents__main">
<UserInfo/>
<PostSection/> -> use if URL /users /{userId}
<FollowSection/> -> use if URL /users/{userId}/follow
<FollowerSection/> -> use if URL /users/{userId}/follower
</div>
</div>
</div>
</template>
<script>
import UserInfo from '~/components/organisms/users/UserInfo'
import PostsSection from '~/components/organisms/users/PostsSection'
import FollowSection from '~/components/organisms/users/FollowSection'
import FollowerSection from '~/components/organisms/users/FollowerSection'
...
What should I do to achieve these?
As the displayed components depends on the current route, you can use the router.
Namely using the nuxtjs nested routes feature. (example of dynamic nested routes in nuxtjs docs)
pages/
--| users/
-----| _id/
--------| follow.vue // contains FollowSection
--------| follower.vue // contains FollowerSection
--------| index.vue // contains UserProfile
-----| _id.vue
// users/_id.vue
<template>
<div class="user">
<div class="user__contents">
<div class="user__contents__main">
<UserInfo>
<NuxtChild
:user="user"
#custom-event="customEventHandler"
/>
</div>
</div>
</div>
<template>
You can reuse the components as nested pages like that:
// pages/users/_id/follower.vue
<script>
// don't create a template for the section
import FollowSection from '~/components/organisms/users/FollowSection'
export default FollowSection
</script>
Note that you don't need to import the child components in _id.vue because nuxt configure vue-router to do so. Also that you can pass props and send events to them like normal components.
All your paths have a common prefix users/. So you can use the pages/users/_.vue component to match any path starting with the users/ that was not matched to any other component.
In this component you can examine $nuxt.$route.params.pathMatch to decide, which subcomponent to show. It will contain part of path after users/:
<template>
<div class="user">
<div class="user__contents">
<div class="user__contents__main">
<UserInfo />
<PostSection v-if="/^\d+$/.test(path)" />
<FollowSection v-else-if="/^\d+\/follow$/.test(path)" />
<FollowerSection v-else-if="/^\d+\/follower$/.test(path)" />
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
path() {
return this.$nuxt.$route.params.pathMatch;
},
},
...
};
</script>
Instead of one page , you can create individual page by setting your project directory structrue like this . It will more easy to handle and modify
pages/
--| users/
-----| _id/
--------| follow.vue
--------| follower.vue
-----| _id.vue

Using a component containing other components within a router-view in Vue.js

I am trying to build a layout using single-file components in Vue.js, with dynamic population and URLs using Vue-router. (I'm using the webpack template via vue-cli as well.)
It works as expected for my app.vue file-- containing the nav, sidebar, page head, and <router-view>-- and the <router-view> content appeared as expected when the correct <router-link> is clicked... until I tried to add subcomponents to the add-load component being called to the <router-view>. Now, nothing appears at all, despite not throwing any errors.
Admittedly, I am not basing my structure on any examples, as I couldn't really find any doing it the way I was hoping to. I wanted to use nested components by calling them like custom elements-- I think this makes the code much easier to read and maintain. I'm not entirely sure how to structure it otherwise, to be honest. Using multiple <router-view>s as siblings to each other seems counterintuitive to me.
I've tried a variety of combinations of how and where to import and call the components, and nothing has worked. The only way I can get any content to load is if I only call a single component for path: '/add-load'. Is it just impossible to use multiple components outside of your base app.vue? I find that hard to believe. Here's what I started with.
From my index.js:
import AddLoad from '#/components/AddLoad'
import AddLoad from '#/components/ProgressSteps'
import Stops from '#/components/Stops'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
components: {
Sidebar,
TopNav,
MobNav,
PageHead
}
},
{
path: '/add-load',
components: {
AddLoad,
ProgressSteps}
}
]
})
From my App.vue file (the multiple component behavior that I'd like to mimic is shown here):
<template>
<div id="app">
<div class="wrapper">
<Sidebar/>
<div class="container-fluid">
<TopNav/>
<MobNav/>
<div class="container-fluid">
<PageHead/>
<router-view></router-view>
</div>
</div>
</div>
</div>
</template>
<script>
import Sidebar from '#/components/Sidebar'
import TopNav from '#/components/TopNav'
import MobNav from '#/components/MobNav'
import PageHead from '#/components/PageHead'
export default {
name: 'App',
components: {
Sidebar,
TopNav,
MobNav,
PageHead
}
}
</script>
From my AddLoad.vue file:
<template>
<div class="add-load">
<div class="content-container container-slim">
<progress-steps/>
<router-link to="#stops">Stops</router-link>
<router-view></router-view>
</div>
</div>
</template>
<script>
import ProgressSteps from '#/components/ProgressSteps'
export default {
name: 'AddLoad',
component: ProgressSteps
}
</script>
Here is a link to a codesandbox, so you can see the full functionality. https://codesandbox.io/s/7k520xk0yq

Vuejs template inheritance

How can I use template inheritance (Like what jade has, extends file.jade and then the blocks with the same name would be overwritten)?
I know that I can do everything with composition, but for components like footer and header which appear on every single page except one or two (e.g.login page) I must write them on every single component. In my app I have a two level navigation and it seems painful to repeat them on every one of those child components :(
I know that I can use jade and then inherit a jade file within my components, but it seems wrong because I would have some jade and some Vue files, is there any other way to do this?
// Component.vue
<template lang="jade">
extends ./StandardLayout
block content
router-view
</template>
// StandardLayout.Vue
<template lang="jade">
div
navbar
div.container
div.spacer
div.row
block content
<template>
What I've settled for, is a layouts folder filled with jade layouts and I use them to extend my components. I used vue-cli with webpack template.
In the most general case if you have to repeat the same HTML over and over, one option you could use is <partial>s.
<partial name="header"></partial>
<div>My content content</div>
<partial name="footer"></partial>
Where you declare partials as
Vue.partial('header', '<h3>This is the title: {{title}}</h3>')
Vue.partial('footer', '<footer>Mini footer</footer>')
However if you are building a Single Page Application the strategy you could follow is to simply have a header and a footer around your <router-view>, here is a jsfiddle that demonstrates how to do.
https://jsfiddle.net/gurghet/vdqutw2y/
<header><h1>
My title: {{title}}
</h1></header>
<p>
<a v-link="{ path: '/foo' }">Go to Foo</a>
<a v-link="{ path: '/bar' }">Go to Bar</a>
</p>
<router-view></router-view>
<footer>Such footer, many links, wow!</footer>
If you know Chinses, please look it
// Base Component
<template>
<div class="base-thing special-class">
<Button />
</div>
</template>
<script>
import Button from './ButtonClick'
export default {
components: { Button }
}
</script>
// Inheriting Component
<script>
import BaseComponent from './BaseComponent'
import Button from './OtherButton'
export default {
extends: BaseComponent
components: {
Button
}
}
</script>
The Button of Child Component will be replaced OtherButton. We can do something in the OtherButton

Categories

Resources