Обзор текущего состояния Next.js Router

Фреймворк Next.js динамично развивается, он сделал огромный скачок вперед, и поэтому многие предыдущие статьи о нем могли устареть. На момент написания этой статьи действует версия 10.2.3. Предлагаю заглядывать в документацию при использовании примеров, так как некоторые рекомендации могут устаревать из-за улучшившегося API.

Появился функционал, о котором вы можете не знать

У тех из вас, кто давно использует Next.js, в проектах наверняка накопился огромный объем кода, и вы просто продолжаете писать его в уже сложившемся стиле. Одни делают это в силу привычки, другие  —  потому что используют свою кодовую базу в качестве каталога фрагментов кода и примеров. Поэтому они рискуют упустить из виду новые крутые функциональные возможности.

Перенаправления сервера и статус «не найдено»

Эти функциональные возможности, как и предыдущие, появились в версии 10. Однако до сих пор возникает много вопросов о том, как сделать перенаправление на сервере. Рекомендую первым делом проверить, используете ли вы современные методы получения данных, потому что только они обновляются и получают новые функциональные возможности.

Вот простой способ сделать перенаправление или показать страницу 404:

// Перенаправление
export function getServerSideProps() {
    return {
        redirect: {
            destination: '/',
            permanent: false,
        },
    }
}

// Страница 404
export function getServerSideProps() {
    return {
        notFound: true,
    }
}

Автоматическое разрешение параметра href

Попробуйте найти в своем проекте next/link, где есть href, а также свойство as. Нашли? Тогда пора обновить эти ссылки. Потому что, начиная с версии 10.x, больше не нужно передавать оба эти параметра. Удалите параметр href и переименуйте as в href: работать будет так же хорошо.

import Link from 'next/link'

// Как было
function MyComponent() {
    return (
        <Link href="/posts/[post]" as="/posts/blog-post">
            Blog post
        </Link>
    )
}

// Начиная с версии v10.x
function MyComponent() {
    return <Link href="/posts/blog-post">Blog post</Link>
}

Функции маршрутизатора клиента

Заметил, что люди часто используют router.push из useRouter. Тоже это делаете в функциях, обернутых в useCallback, или просто внутри useEffect? Тогда придется упомянуть об этом в зависимостях, как в следующем примере:

// pages/params.jsx
import { useEffect, useCallback } from 'react'
import { useRouter } from 'next/router'

function MyComponent() {
    const { push } = useRouter()
    const handleClick = useCallback(() => {
        // какая-то логика
        push('/profile')
    }, [push])

    useEffect(() => {
        if (...) {
            push('/sign-in')
        }
    }, [push])

    return <>...</>
}

Но это совершенно необязательно, потому что эти функции не обновляются через повторные отображения, зато правила ESLint будут вынуждать вас это делать. Решения здесь довольно простые: использовать функции маршрутизации из Router. Это сделает ваш код чище и приятнее. 😉

// pages/params.jsx
import Router from 'next/router'

function MyComponent() {
    const handleClick = useCallback(() => {
        // какая-то логика
        Router.push('/profile')
    }, [])
    
    useEffect(() => {
        if (...) {
            Router.push('/sign-in')
        }
    }, [])
  
    return <>...</>
}

Иногда при работе с маршрутизатором нужно получить текущее имя пути. Но у react/router в этом поле хранится динамическое представление маршрутизатора, и оно совпадает с полем маршрутизатора. Тогда создаем эти параметры самостоятельно помощью asPath. Я сделал обертку поверх useRouter и всегда имею доступ к свойствам, когда они мне нужны.

// lib/router.js
import { useRouter as useNextRouter } from 'next/router'

export function useRouter() {
    const router = useNextRouter()
    const [pathname, queryString = ''] = router.asPath.split('?')

    return Object.assign(router, { pathname, queryString })
}

Обертка предоставляет удобные свойства, которые используются в ситуации, когда нужно обновить текущий маршрут.

// pages/params.jsx
import Router from 'next/router'
import { useRouter } from 'lib/router'

function MyComponent() {
    const { query, pathname } = useRouter()
    
    function handleSubmit(evt) {
        const params = new FormData(evt.currentTarget).getAll("fruits");
        evt.preventDefault();
        Router.replace(
            { pathname, query: { ...query, params }},
            null,
            { shallow: true }
        )
    }
    
    return (
        <form>
            {...}
            <button type="submit">Apply</button>
        </form>
    )
}

А знаете, почему второй параметр в Router.replace имеет значение null? Начиная с версии 10.x, благодаря автоматическому разрешению есть возможность этот параметр пропустить.

Имея такую функциональную возможность, как автоматическое разрешение, было бы естественно задействовать ее в next/router, но это маленько неудобно в API. Ведь, когда требуется передать опции options, следует передавать динамический маршрут в первый аргумент и полный путь во второй в более ранних версиях, а в Next.js эти три аргумента сохранялись для обратной совместимости. Хорошо, что есть небольшие удобные функции, которые упрощают использование функций маршрутизатора:

// lib/router.js
import NextRouter from 'next/router'

function push(url, opts) {
    return NextRouter.push(url, null, opts)
}

const Router = {
    ...NextRouter,
    push,
}

export default Router

// pages/params.jsx
import Router, { useRouter } from 'lib/router'

function MyComponent() {
    const { query } = useRouter()
    
    function handleSubmit(evt) {
        const params = new FormData(evt.currentTarget).getAll("fruits");
        
        evt.preventDefault();
        Router.push({ pathname, query: { ...query, params } }, { shallow: true });
    }
    
    return <>{...}</>
}

Заключение

Спасибо, что дочитали статью до конца. Полный код найдете на GitHub. Всегда есть способ улучшить хорошие инструменты. А небольшие улучшения способны здорово помочь разработчику и его проекту.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Pavel Mineev: An Overview on the Current State of Next.js Router

Предыдущая статьяКак сделать калькулятор на C
Следующая статья7 признаков того, что вы  -  опытный разработчик