learning javascript development using next.js. I have created a skeleton project, with following structure. what is the best way for development. create model, controllers? where should be the model and controller folders be? or is there a different way to do this in react world?
app
> .next
> node_modules
> pages
> api
> app.js
> public
> src
TL;DR Since nextjs does not impose conventions at this level, it's totally up to you and your team to decide what architecture to adopt and how to implement it and organize it. You can draw inspiration from an MVC-like framework like https://blitzjs.com/, which successfully brings a lot of concepts from the now old and boring Ruby on Rails.
If you think about it, MVC on the server-side fits very well with nextjs.This is how I look at it from a classic Model-View-Controller perspective:
A Controller manipulates your model based on user interaction, stiching together the results and passing them to the view.
In nextjs' terms, pages do fit very well this responsibility, although they do a bit more than that. You can put models wherever you want, lots of entry-level tutorials drop them under lib/.
Consider this example where I've got a page to show users profiles, in this case I'm also interested in doing Static Site Generation. Side note, I'm writing this off the top of my head, so be aware of copy and pasting to run it, it will prolly not work.
// under app/pages/users/[userId].js
import UserProfile from 'components/user-profile';
import User from 'models/user';
export default UserProfile;
// both at request time and build time, preps the props passed to the View.
export const getStaticProps = async ({params}) => {
const user = await User.find(params.id);
return {
props: { user }
}
}
// instructs next to render all user profiles using SSG
export const getStaticPaths = async () => {
const users = await User.findAll();
const paths = users.map(user => `/users/${user.id}`);
return { paths, fallback: false };
}
My team and I like to go a step further, and follow this structure, although we do not interface a database, but the file system and a few third party APIs over REST and GraphQL. Little secret: we tell nextjs which files to look for to load pages, otherwise it goes a bit cray cray.
project
└── app
├── components
│ ├── user-profile.test.tsx
│ └── user-profile.tsx
├── models
│ └── user.ts
│ └── user.test.ts
└── pages
└── users
├── [userId].page.ts
├── get-static-paths.test.ts
├── get-static-paths.ts
├── get-static-props.test.ts
└── get-static-props.ts
React is not an MVC framework, so architecturally it's a bit different. You can still structure it in a MVC way if you prefer, follow flux / redux patterns etc. Something to note that's different about react is it's unidirectional data flow unlike MVC where data is bound between the view and controller.
You also mentioned just starting to learn JS, I would recommend learning the fundamentals of javascript before diving into next.js, maybe even start with react. There are some things to consider when developing a server rendered app, but next definitely does a good job at making it easy.
Next.js also has their site open sourced here: https://github.com/zeit/next-site
I haven't looked at the code too much, but there are some patterns you could look for there.
Related
There are SSR-related problems with several pages in Next.js project that results in errors on npm run build and prevent the project from being built:
pages/
foo/
bar/
[id].jsx
index.jsx
index.jsx
...
For example, bar:
export function getStaticProps() {
return someApiCallThatCurrentlyFails()
...
}
export default function Bar() {...}
As a quick fix, it may be convenient to just not build bar/*.* pages and make routes unavailable.
Can pages be ignored on Next.js build without physically changing or removing page component files in the project?
You can configure the pageExtensions in the next.config.js.
// next.config.js
module.exports = {
pageExtensions: ["page.js"],
}
After configuring this, the only pages with *.page.js will be considered in the below given directory structure.
pages/
├── user
│ └── setting
│ ├── index.js
├── _app.page.js
├── _document.page.js
├── list.page.js
└── theme.ts
Custom file ignores patterns that are not supported yet. You can visit the PR created here, and the solution given here. This is the most satisfactory solution so far.
#Mathilda Here from Nextjs docs: it's necessary for all pages including _app, _document, etc.
https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions
Changing these values affects all Next.js pages, including the following:
- middleware.js
- pages/_document.js
- pages/_app.js
- pages/api/
For example, if you reconfigure .ts page extensions to .page.ts, you would need to rename pages like _app.page.ts.
I have a React site currently in JavaScript/ES that we are converting to TypeScript (🎉). We use the Container and Presentational breakdown, so each component will have either a Container and a Presentational file, or for simpler components just a Presentational file.
Over the course of a build, we will typically build "static" Presentational screens/components first, get them to pass design QA, and then make them dynamic by adding a Container. That means that the Presentational files are usually built first. Containers are built later, if needed.
Our directory structure looks like this:
components
├── SomeComponent
│ └── index.js
│ └── index.jsx
├── SomeOtherComponent
│ └── index.jsx
^^ Here we have SomeComponent which has Container+Presentational, while SomeOtherComponent has only Presentational.
This structure provides us with several benefits:
Imports are short and not "doubled": import SomeComponent from '../screens/SomeComponent' (not import SomeComponent from '../screens/SomeComponent/SomeComponent')
Imports do not need to change when Containers are eventually added. If only a Presentational .jsx file exists, the import above works. When/if a Container .js file is added, it then takes precedence over the .jsx, so the same import now targets the new file by virtue of it existing. (Webpack resolve.extensions = ['.js', '.jsx'])
No need to rename the presentational component file when a Container is added. This keeps git history nicely intact.
Quick & easy visual identification of which file is Container vs Presentational. Presentational is .jsx because that's where our JSX lives.
We use react/jsx-filename-extension to enforce that JSX should only be in .jsx files. This gives our devs get an eslint warning if they try to write JSX in a .js Container file. This enforces the concept that Containers should do business logic (in JS), while Presentationals should just output "dumb" markup (in JSX).
Works great!
The only caveat, which in my opinion is very minor, is: In order for this to work, in our Containers we import the Presentational like so: import SomeComponent from './index.jsx'. I say this is a caveat simply because it's the only place where we import using a complete file extension. No big deal...
In TypeScript, I'd love to just change those file extension from js/jsx to ts/tsx like so:
components
├── SomeComponent
│ └── index.ts
│ └── index.tsx
├── SomeOtherComponent
│ └── index.tsx
But we have a problem: In TypeScript we cannot use file extensions in an import (to the best of my knowledge). So this breaks our ability to execute the "caveat," and seems to force us to have different filenames for the Containers vs Presentationals. 😭
How does your team solve this dilemma?
I have searched for conventions on how other teams navigate this issue, and have come up empty-handed.
I am open to criticism of any part of our convention here, and I invite discussion. This is a higher-level issue that also affects the lower-level day-to-day of our devs. All ideas are good ideas (except the ones that suck)!
A few ideas:
A) ts-ignore
Use // #ts-ignore above the import SomeComponent from './index.tsx' line to mute the error. This feels like bad practice, but what would be the consequences?
Pros:
Allows use of index.ts and index.tsx to mimic our previous setup
No need to rename presentationals when a container is added
No need to change imports when a container is added
Cons:
Feels like bad practice (🚩)
Have to remember to add it and instruct other devs to add it
Requires we add '#typescript-eslint/ban-ts-ignore': 'off' to our eslint conf, which by extension means eslint will no longer prevent people from using #ts-ignore anywhere they want. 😬
What else might this break? Does it kill intellisense? What other havoc would this wreak?
B) PascalCase Presentationals
components
├── SomeComponent
│ └── index.ts
│ └── SomeComponent.tsx
├── SomeOtherComponent
│ └── index.tsx
Pros:
Seems kindof conventional. Some teams use PascalCase for all component names as convention.
No need to change imports when a container is added.
Cons:
Requires renaming presentational file when container is added (messes with git history).
Not as easy to instantly identify which file is container vs presentational. There's a (small) logical leap that must be taken in your mind: "That one is named after the component and when they are named after a component they are presentational").
The two Container/Presentational files will not always be "next to" eachother in the directory. If another file named index.test.ts or index.stories.tsx or styles.js exists, it might be in-between index.ts and presentational.tsx
C) presentational.tsx
components
├── SomeComponent
│ └── index.ts
│ └── presentational.tsx
├── SomeOtherComponent
│ └── index.tsx
Pros:
No need to change imports when a container is added.
Instantly obvious which file is presentational. (Solves problem with B above)
Cons:
Requires renaming presentational file when container is added (messes with git history).
The two Container/Presentational files will not always be "next to" eachother in the directory. If another file named index.test.ts or index.stories.tsx exists, it will be in-between index.ts and presentational.tsx
D) index.presentational.tsx
components
├── SomeComponent
│ └── index.ts
│ └── index.presentational.tsx
├── SomeOtherComponent
│ └── index.tsx
Pros:
No need to change imports when a container is added.
Instantly obvious which file is presentational. (Solves problem with B above)
The two Container/Presentational files will always be "next to" eachother in the directory. (Solves problem with B & C above)
Cons:
Requires renaming presentational file when container is added (messes with git history).
Long filename (not sure I really care about this)
We have developed a React component library for internal use. The library uses webpack to bundle everything into a bundle.js. The library is exposed as an NPM module for use by our applications, which so far has been working great.
Recently we added a Grid component that has some very large external dependencies. Even though a few of our applications won't need this component from the library, we decided to include it in the final bundle. This can make a huge difference in bundle size, so I followed Webpack's code splitting guide to break out the Grid component using a dynamic import. For our applications that have this library installed as an NPM module, the component library bundle now looks like this in node_modules:
├── node_modules/
│ ├── [our component library]/
│ │ ├── Grid.js # Asynchronously loaded Grid component
│ │ ├── bundle.js # Main bundle
│ │ └── ... other files in component library bundle
When building the application (also with Webpack), the Grid component doesn't seem to be getting included as its own file in the final bundle. In fact, it doesn't get included at all:
├── dist/
│ ├── main.js # App bundle
│ ├── vendor.js # Vendor bundle created with CommonChunksPlugin
│ └── ... other files in application bundle but no Grid.js
When trying to load a page in the browser that is using the asynchronously loaded Grid component, Webpack returns the following error:
Error: Loading chunk 0 failed.
at HTMLScriptElement.onScriptComplete (bundle.js:757)
Essentially, this says the Grid component chunk can't be found. What am I doing wrong? Is it even possible to code split a React component library like this?
I used https://webpack.js.org/guides/code-splitting/#dynamic-imports both import() and require.ensure syntax for dynamic imports. Both worked well. Check this project of mine for good example https://github.com/croraf/myboiler.
This project also is an example: https://github.com/croraf/nutrients-calculator. But I complicated dynamic import part a bit. Check https://github.com/croraf/nutrients-calculator/blob/master/frontend/src/App.js and https://github.com/croraf/nutrients-calculator/blob/master/frontend/src/routes/util/DynamicRoute.js files.
We decided to take a different route with this. Instead of including the Grid component with the rest of the components we split if off into its own npm module. I still see a use case for dynamically importing react components in a library but we felt this was a better solution to our problem. With this solution, the only real downside is that the application will now need to add 2 npm modules instead of one if it needs to use the Grid component and our standard component library. Since our application does code splitting by route with webpack + dynamic imports, the Grid code only gets bundled into the route that needs it.
one possible reason is babel transpiled your code from import("..") to require. check transpiled file to confirm this.
if this is the case and you are using preset, you can add this to disable this behavior:
"#babel/preset-env",
{
**"modules": false,**
"targets": {
"esmodules": true
}
}
],
Up until now I have been building my application using the following structure.
/src
/components
/shared
/messagebox
/alertbox
/Home
/About
So as you can see I have shared components which are components being used on other components / pages. and I have the concept of Home, About which are also components (which translate to viewable pages) as everything in Angular is now supposed to be a component - right?
Does anyone have a better implementation of the structure of a NG 1.5 app?
I now need to create some filters and as far as I am aware I cannot hang these off of a component, so where is the based place for putting this type of file?
Also other things that come to mind are constants, services etc.
There is no recommendation on Angular docs site as far as I can see.
The one which i used to follow for my project is :
project
/www
/assets
/components // directives
/accordion
/accordion.html
/accordion-directive.js
/accordion-controller.js
/accordion-controller-test.js
/core // shared services
/route
/router-service.js
/sections // pages we build by adding components
/registration-page
/registration.html
/registration-controller.js
/registration-controller-test.js
app.js
index.html
index-controller.js
app-configuration.json // for keeping constants
this might help you.Please check.
I know that there are few questions about the react-redux project code structure but I am hoping to discuss a different approach. So the main libraries we are going to use are : webpack - react - redux -mocha - karma.
The classic folder structure for this seems to be:
js
├── actions
│ ├── action-for-componentA.js
├── components
│ ├── componentA.js
├── reducers
│ ├── reducer-for-componentA.js
└── stores ...
This seems to be what everyone else and all the generators out there are doing. But I feel like this is not the react way of structuring a project. The focus should be on the component and not on the individual constructs of react or redux. I would like to think about it in this way, when you need to change a button in PageX which is contained in a component hierarchy that starts with ComponentX -> ChildOfX - I should be able to traverse the directories in this exact same way.
Rather than having a components folder with all components thrown inside it I would rather have something like :
js
├── PageX
│ ├── action-for-pagex.js
├── componentX.js
├── containerX.js
├── reducerX.js
├── children
├── childrenC
├── childrenB
├── componentB.js
├── reducerB.js
├── ...
├── PageY
│ ├── ...
├── PAgeZ
│ ├── ...
This will be easier to traverse and it makes more sense when you think in "react". Can anyone see anything that might go wrong with this approach?
Related reading : http://engineering.kapost.com/2016/01/organizing-large-react-applications/
TL;DR
There is no standard or proper way structuring your application's code; it's mostly about your taste.
I will give you an answer due to my experience with React and Redux. Right now, I am involved in a huge project using the R&R stack. Our tech team had spent a great amount of time talking about folder structure since we should keep a linear and scalable way of coding.
Basically, we are using hybrid approach mixing the two examples your have provided. The second approach is referenced as "Folder structure by feature", the first one is called "Folder structure by type".
Since the React/Redux stack uses a standardised typeset of files it's quite easy to keep your application tree as tidy and scalable as possible.
Our technical team has agreed that:
Each Main Component stands out under the pages folder.
Each page may have some sub components as well.
Components are using a component.js entry point and may use a reducer as well
Sub components that are actually inherited and used from more than one parent components are placed under the shared folder
We also maintain some helper functions folder and a folder that holds some constants and utilities. This is mostly because our action dispatchers use common actions in our application lifecycle.
The application is bootstrap by a single container, a single store and simple entry point (app.js).
We use ES6 import statements to hold everything in place.
Here is a schematic version of our file structure:
constants
-- const.js
-- const2.js
helpers
--helperfunc1.js
shared
--Shared Element1
--- component.js
--- reducer.js
--Shared Element2
--- component.js
--- reducer.js
specs
-- spec1.js
-- spec2.js
pages
-Page1
-- Subpage1
--- component.js
--- reducer.js
-- Subpage2
--- component.js
--- reducer.js
-- component.js
-- reducer.js
-Page2
-- component.js
-- reducer.js
...
container.js
app.js
reducer.js
As we kept coding, adding features and maintaining our application, we wrote down some pros of this approach:
- Filenames are pretty short and easy to read.
- The file structure is easy to follow.
- Testing has made damn simple as importing the component and the reducer from a folder
- Naming bottlenecks have been thrown away.
- We had less naming issues and under the same folder.
- Once more, TableDataComponent.js is more verbose and hard to follow than table/component.js.
- Since React nested components are an essential part of the framework, the folder structure tracks down this logic. Inner levels of code are inherited from the upper rendered elements.
- Action reducers are also smoothly bootstrapped .
I would suggest spending some time reading this wonderful Reddit thread and this article as well, some awesome points are mentioned above.
I've came up with this structure and it works pretty well