Howler | 다음은 기본적인 완전한 창고입니다.js 응용 프로그램은 API 라우팅 및 React 쿼리를 사용합니다.
47149 단어 reactreactquerytypescriptnextjs
This is not a how to build post, but me writing down what and how I made stuff. A learning journal if you may.
스택
설계
우선, 나는 거의 항상 디자인에서 나의 프로젝트를 시작한다.나는 디자이너가 아니지만, 간단한 원형은 내가 정력을 집중하는 것을 도울 수 있다.보통 Figma에 만들어집니다.
The design is obviously inspired by twitter. Made this in Figma so that I can have a reference to follow in code as close as I can.
설치 프로그램
이 프로젝트에서 나는 넥스트로 나의 손을 더럽히고 싶다.js
다행히도 다음은js는 이미 대량의 템플릿을 가지고 있습니다.
그래서 나는 그들의 typescript를 사용하여 시간을 절약할 것이다. 비록 그 안에 typescript를 추가하는 것은 매우 간단하지만.
프로젝트 초기화
npx create-next-app --example with-typescript howler
유형 스크립트지금 나는 나의 tsconfig를 수정할 것이다.json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@/*": ["*"],
"@/api/*": ["/pages/api/*"],
},
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
나는 Typescript를 배울 때 엄격한 모드"strict": true
를 열 때 더욱 유용하다는 것을 발견했다.이것은 너로 하여금 타자를 치는 모든 것을 내놓게 했다.컴파일러 옵션은 더 깨끗한 외관 가져오기를 위한 나의 선호일 뿐입니다.
다음을 입력할 필요가 없습니다.
import Example from `../components/Example`
//or worst case.
import Example from `../../../components/Example`
알았어!네가 어디에서 그것을 필요로 하든지 간에.import Example from `@/components/Example`
순풍 CSS처음에는 좀 짜증났지만 CSS 기반 유틸리티의 프레임워크를 사랑하게 되었다.
npm install -D @tailwindcss/jit tailwindcss@latest postcss@latest autoprefixer@latest
// tailwind.config.js
module.exports = {
purge: [
'./src/pages/**/*.{js,ts,jsx,tsx}',
'./src/components/**/*.{js,ts,jsx,tsx}',
],
darkMode: false,
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
Css 구성 게시// postcss.config.js
module.exports = {
plugins: {
'@tailwindcss/jit': {},
autoprefixer: {},
}
}
인증
Next에서 개방형 인증을 수행합니다.js는 Nextuth를 사용합니다.js.
나는 그들의 문서를 연결할 것이다. 아주 잘 썼다.
NextAuth Docs
나는 나의 OAuth로 Github을 사용할 것이다.문서가 끝난 후 세션 데이터는 이름, 이메일, 이미지만 포함됩니다.그러나 세션에 사용자github '탭' 을 추가하고 전방에서 접근할 수 있기를 바랍니다.
이 점을 깨닫는 데 시간이 좀 걸렸지만, jwt 리셋의 프로필 매개 변수에서 'tag' 와 다른 데이터를 얻을 수 있습니다.이렇게
API 방면
import NextAuth, { InitOptions } from 'next-auth'
import Providers from 'next-auth/providers'
import { NextApiRequest, NextApiResponse } from 'next/types'
import User from '@/backend/model/userModel'
import dbConnect from '@/utils/dbConnect'
import { customUser } from '@/types/Model.model'
const options: InitOptions = {
providers: [
Providers.GitHub({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
],
database: process.env.MONGODB_URI,
session: {
jwt: true,
},
callbacks: {
//Add userTag to User
async session(session, user: customUser) {
const sessionUser: customUser = {
...session.user,
userTag: user.userTag,
id: user.id,
}
return Promise.resolve({ ...session, user: sessionUser })
},
async jwt(token, user: customUser, profile) {
let response = token
if (user?.id) {
//Connect to DataBase
dbConnect()
//Get User
let dbUser = await User.findById(user.id)
//Add UserTag if it doesn't already exist
if (!dbUser.userTag && profile.login) {
dbUser.userTag = profile.login
await dbUser.save()
console.log('No tag')
}
response = {
...token,
id: user.id,
userTag: dbUser.userTag,
}
}
return Promise.resolve(response)
},
},
}
export default (req: NextApiRequest, res: NextApiResponse) =>
NextAuth(req, res, options)
그 다음에'초기 설정이 완료되었다고 가정하기'는 갈고리를 통해 세션을 검증하고 얻으며'로그인'또는'로그아웃'을 가리키는 링크를 통해 일을 전면에서 일하게 한다.반응면
import { useRouter } from 'next/router'
const Home: FC = () => {
// session - contains our user data , loading - self explanatory
const [session, loading] = useSession()
const route = useRouter()
// Redirects you if you are logged in
useEffect(() => {
session && route.push('/home')
}, [session])
// Render if session is loading
if (loading || session) {
return (
<>
<Head>
<title>Loading...</title>
<link rel="icon" href="/pic1.svg" />
</Head>
<Loader />
</>
)
}
// Render if there is no session
return (
<PageWarp title={'Welcome to Howler'} splash>
<LoginPage />
</PageWarp>
)
}
export default Home
국가 관리
애플리케이션 전역 상태를 사용한 React Context API 추적
암시적 모드나 탐색 등의 상태에 사용되며 React Query를 사용하여 비동기식 데이터를 캐시에 저장합니다.
레드ux 사용에 관한 논쟁이었지만 SWR과 React Query를 들었을 때 생각을 바꿨다.결국 React Query를 사용했습니다. 캐시된 데이터를 엿볼 수 있는 개발 도구가 있기 때문입니다.
반응 조회
일이 이렇다.
전 세계 국가와 같이, 우리는 그것을 우리의 전체 응용 프로그램으로 포장해야 한다.
QueryClientProvider
와 이 아이템client={queryClient}
을 가지고 있습니다.반응 질의에서 가져옵니다.내가 이렇게 하는 동시에 dev 도구 덮어쓰기 추가
import { QueryClientProvider, QueryClient } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
//React Query Connection
const queryClient = new QueryClient()
const QState: FC = ({ children }) => {
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
export default QState
그리고 우리는 우리의 전 세계 국가 공급업체를 둘러싸고 있을 수 있다.반응 어경
import React, { FC, useReducer, createContext } from 'react'
import { InitialHowlState, HowlReducer, howlNav } from '@/types/Howl.model'
import QState from @/components/context/QState
// Create Context
const HowlCtx = createContext<HowlContext>({} as HowlContext)
//Reducer
const howlReducer: HowlReducer = (state, action): InitialHowlState => {
switch (action.type) {
//Navigation State
case 'NAVIGATION':
return { ...state, nav: action.payload }
default:
return state
}
}
//INITIAL STATE
const initialState: InitialHowlState = {
nav: 'home',
}
const HowlState: FC = ({ children }) => {
const [state, dispatch] = useReducer<HowlReducer>(howlReducer, initialState)
//ACTIONS
const setNavigation = (nav: howlNav) => {
dispatch({ type: 'NAVIGATION', payload: nav })
}
return (
<QState >
<HowlCtx.Provider value={{ state, setNavigation }}>
{children}
</HowlCtx.Provider>
</QState >
)
}
export default HowlState
React 질의 사용
Reminder React Query does not replace FETCH API or AXIOS
React 조회에서 데이터를 얻을 때, 우리는 갈고리
useQuery
를 사용합니다.일은 이렇다.import { useQuery } from 'react-query'
import axios from 'axios'
const App = () => {
const fetcher = async (_url: string) => {
const { data } = await axios.get(_url)
return data
}
// First argument Naming the data to be cached | Second argument your fetcher. Where your fetch api goes.
const { isLoading, isError, data, error } = useQuery('name', fetcher('https://api.example'))
}
More Info 그들의 서류에 있다.나는 이런 맞춤형 갈고리를 한 무더기 만들 것이다.그래서 너는 그것들을 다시 사용할 수 있다.
Typings on useQuery hooks are just like react hooks 'Generics'
import { useQuery } from 'react-query'
import axios from 'axios'
import { HowlT, HowlUser } from '@/types/Howl.model'
export const fetcher = async (_url: string) => {
const { data } = await axios.get(_url)
return data
}
export const useGetHowls = (options?: UseQueryOptions<HowlT[]>) => {
return useQuery<HowlT[]>('howls', () => fetcher('/api/howl'), options)
}
export const useGetHowlById = (_id: string) => {
return useQuery<HowlT>(['howls', _id], () => fetcher(`/api/howl/${_id}`), {
enabled: false,
})
다른 갈고리처럼.import { useGetHowls } from '@/hooks/queryHooks'
const App = () => {
const { data, isLoading } = useGetHowls()
return(
<div>
{data?.map((howl) => <Howl {...howl}/> )}
</div>
)
}
게시물을 업데이트하거나 삭제하거나 만들 때useMutation을 사용하고 사용자 정의 연결을 만들어야 합니다.그들의 서류에는 더 좋은 해석이 있다.useMutation 첫 번째 파라미터는fetch 함수이고 두 번째 파라미터는 부작용 대상이다.
다음 예는 요청이 성공할 때 터치하는 OnSuces 부작용을 가진post 요청을 보여 줍니다.최신 데이터를 얻기 위해 새로 발표된 howl을 기존 캐시 데이터
setQueryData
에 추가하고 실효 invalidateQueries
합니다.export const useCreateHowl = () => {
const queryClient = useQueryClient()
return useMutation(
(newHowl: { howl: string }) => axios.post('/api/howl', newHowl),
{
onSuccess: (data) => {
queryClient.setQueryData<HowlT[]>('howls', (old) => [
data.data,
...old!,
])
// console.log(data)
queryClient.invalidateQueries('howls')
},
}
)
}
api에 자신이 있다면 onMutate
부작용을 사용하여 더욱 낙관적인 업데이트를 할 수 있습니다. 요청 결과를 얻기 전에 요청이 성공하든 안 하든 데이터를 조작할 수 있습니다.잼 더미의 "A"!REST API
다음 API 라우팅
switch가 아닌 넥스트 연결 패키지를 사용하여 Express 응용 프로그램 문법을 시뮬레이션할 것입니다.
이전
export default function handler(req, res) {
switch (method) {
case 'GET':
// Get data from your database
break
case 'PUT':
// Update or create data in your database
break
default:
return
}
}
그 다음Create a middleware first. Passing in your database connection function to get access to it when using this middleware
import dbMiddleware from './db'
import nextConnect from 'next-connect'
export default function createHandler(...middlewares: any[]) {
//Connect to DB
return nextConnect().use(dbMiddleware, ...middlewares)
}
//API Route
import createHandler from '@/backend/middleware'
//protect is a middleware I made for verifying session login with NextAuth.js
import { protect } from '@/backend/middleware/protect'
import { addHowl, getHowls } from '@/backend/controller/howlController'
const handler = createHandler()
handler.get(getHowls)
handler.post(protect, addHowl)
export default handler
나도 Express 응용 프로그램처럼 MVC 디자인 모델을 따를 수 있기 때문에 나의 API는 더욱 모듈화할 수 있다.Controllers looks like this. With comments as a reminder of what they do.
//@desc Get Howls
//@route GET /api/howl
//@access Public
export const getHowls = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const howls = await Howl.find({})
.populate('user', 'name image userTag')
.sort({ createdAt: -1 })
return res.status(200).json(howls)
} catch (error) {
res.status(404)
throw new Error('Error! No howls found')
}
}
케이크 위의 설탕 크림
정교한 애니메이션이 없는 개인 프로젝트는 무엇입니까?
react의 대부분 항목에서, 나는 항상 프레임 운동을 사용한다.간단한 애니메이션(예를 들어 입구 애니메이션이나 페이지 전환)을 사용하면 쉽게 시작할 수 있고 이 복잡한 애니메이션 프레임워크를 사용하여 언제든지 게임을 시작할 수 있습니다.
새로운 기능?
결론
타자 원고가 너무 좋아요.🦾 TS의 주요 갈고리는 개발 환경에서 버그가 발생하는 것을 방지하는 것이지만, 나는 암시를 더욱 좋아한다!
이 문제는 사람을 흥분시킨다💥 당신이 조직한 전 세계 상태의 사고방식을 바꾸세요.당신의 본지와 다른 지역을 분리하는 것은 매우 의미가 있습니다.
다음.그저💣 바닐라로 리얼리티 프로그램을 만들어서 리얼리티를 하는 것은 더 이상 상상할 수 없습니다.Vercel의 배치는 매우 순조롭다. 나 같은 사람에게 CICD는 단지 그들의 프로젝트를 거기에 나타나게 하려고 할 뿐이다!
아직 배워야 할 것이 많지만, 나는 매우 즐겁게 놀았다!
링크
Github Repo
인사!Live Demo
이게 다야!왔어!
Reference
이 문제에 관하여(Howler | 다음은 기본적인 완전한 창고입니다.js 응용 프로그램은 API 라우팅 및 React 쿼리를 사용합니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/imervinc/howler-a-basic-fullstack-next-js-app-using-its-api-routes-w-react-query-51h7텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)