Когда я начал изучать React, я познакомился с React Router и узнал, как эта библиотека может помочь нам в веб-приложении.
Я разрабатывал веб-приложение, думая в первую очередь о мобильных устройствах, где во время изменения размера экрана кнопка действия должна быть изменена вместе с URL-ссылкой.
Прежде чем включать это в свое приложение, я создал приложение React, чтобы проверить свою идею и протестировать только это поведение. Давай проверим.
Создание нового приложения React
Первый шаг - создать новое приложение React для игры. Я настоятельно рекомендую, когда вы что-то разрабатываете и сталкиваетесь с какой-то конкретной задачей, в которой вы не знаете, как это сделать, запустить отдельное приложение для реагирования и попробовать разные эксперименты, пока не найдете хорошее решение для своего проекта.
Довольно легко проверить и протестировать решение по отдельности.
Чтобы создать новое приложение React, следуйте инструкциям в официальной документации.
npx create-react-app test-router-sreen
cd test-router-sreen
npm start / yarn start
Давайте начнем создавать простой JSX, в нашем «приложении» будет всего три компонента: устройство / мобильное устройство / приложение, последний - наш индекс.
В своем тестовом приложении я использовал Material UI (начните здесь), потому что это библиотека, с которой я люблю работать, но вы можете использовать только HTML и вставить CSS, если хотите.
Тест приложения
Идея этого приложения состоит в том, чтобы иметь экран с кнопкой «Показать мобильный», и когда пользователь выполняет действие «щелкнуть» по этой кнопке, компонент отображается с сообщением: «Я отображается только на мобильном устройстве» и при перемещении размера экрана кнопка меняется на «Показать устройство», затем при нажатии на кнопку отображается сообщение «Я обрабатываюсь только на устройстве».
//App import React, { Component } from 'react' import Button from '@material-ui/core/Button' import Hidden from '@material-ui/core/Hidden' class App extends Component { render() { return ( <div className="App"> <Hidden xsDown> <Button variant="contained" color="primary"> Show Device </Button> </Hidden> <Hidden smUp> <Button variant="contained" color="secondary"> Show Mobile </Button> </Hidden> </div> ); } } export default App;
Я применил пользовательский интерфейс Hidden of Material для отображения / скрытия кнопки в точках останова.
Компоненты Device и Mobile были созданы очень просто.
//Device// import React, { Component } from 'react'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; class Device extends Component { render() { return ( <div> <Typography component="h2" variant="h1" gutterBottom> I am rendered only on Device :) </Typography> </div> ); } } export default Device; //Mobile// import React, { Component } from 'react'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; class Mobile extends Component { render() { return ( <div> <Typography component="h2" variant="h1" gutterBottom> I am rendered only on Mobile :) </Typography> </div> ); } } export default Mobile;
Запуск React Router
Теперь задача заключается в следующем:
- Чтобы показать компонент "Мобильный" при нажатии кнопки "Мобильный" или для отображения компонента "Устройство" при нажатии кнопки "Устройство", изменив URL-адрес на / mobile или / device .
- При перемещении размера экрана с отображаемым компонентом маршрут должен автоматически переключаться между / mobile ’на индексирование« / »или / device’ на индексирование «/».
Чтобы использовать React Router в нашем проекте, мы используем инструкции из документации для установки пакета.
npm install react-router-dom
or yarn addreact-router-dom
Прежде всего, давайте создадим маршрут внутри кнопок Mobile и Device. Мы не можем забыть импортировать зависимости.
//App ... import { BrowserRouter as Router, Route, Link } from "react-router-dom" import Mobile from './Mobile' import Device from './Device'
И включите Route в код с Link на кнопках.
class App extends Component { render() { return ( <Router> <div className="App"> <Hidden xsDown> <Link to='/mobile'> <Button variant="contained" color="primary"> Show Device </Button> </Link> </Hidden> <Hidden smUp> <Link to='/mobile'> <Button variant="contained" color="secondary"> Show Mobile </Button> </Link> </Hidden> <Route path="/device" component={Device} /> <Route path="/mobile" component={Mobile} /> </div> </Router> ); } } export default App;
Хорошо, маршрут для компонентов привязан к кнопке, когда мы нажимаем на рендеринг компонента и меняем путь, но сейчас отсутствует важный момент. Во время изменения размера экрана измените маршрут, но не отобразите фактический компонент.
Чтобы увидеть событие изменения размера на экране и отправить действие, используйте пакет React Resize Detector.
Установка…
npm i react-resize-detector
// or
yarn add react-resize-detector
В документации к этому пакету можно увидеть, что мы можем применять различные формы, такие как шаблон обратного вызова, шаблон дочерней функции, шаблон дочернего компонента и т. Д. Шаблон дочернего компонента, чтобы знать точную точку, в которой при изменении размера следует применить перенаправление и изменить маршрут URL-адреса.
Добавление перенаправления при импорте response-router-dom и React Resize Detector.
//App ... import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom" import ReactResizeDetector from 'react-resize-detector' ...
Сначала мы включаем the<ReactResizeDetector />
в дерево DOM и проверяем точку останова UI материала для мобильных устройств, где изменение кнопки с устройства на мобильное устройство меньше ширины 600 пикселей.
Используя эту информацию, добавьте константу с правилом изменения размера, используя тернарный оператор и Redirect do Router, чтобы изменить маршрут в соответствии с с из размера экрана. Мы создали функцию CheckIfRedirect
.
const CheckIfRedirect = ({width, height}) => { const path = window.location.pathname console.log('resize', width, height, path) if ((width < 600 && path === "/device") || (width >= 600 && path === "/mobile")) { return <Redirect to={{pathname: "/"}} /> } else { return <></> } }
В соответствии с этим правилом он специфичен, когда ширина меньше 600 пикселей, а путь '/ device' или с больше или равен 600 пикселей и путь '/ mobile ', а затем перенаправить на путь: ' / ' в данном случае - это наш индекс без определенного компонента.
Информацию window.location.pathname
можно найти, просматривая консоль браузера.
Итак, разделив код внутри функции App на другую функцию, мы назвали Body.
const Body = () => ( <div className="App"> <Hidden xsDown> <Link to='/device'> <Button variant="contained" color="primary"> Show Device </Button> </Link> </Hidden> <Hidden smUp> <Link to='/mobile'> <Button variant="contained" color="secondary"> Show Mobile </Button> </Link> </Hidden> <Route path="/device" component={H1Device} /> <Route path="/mobile" component={H1Mobile} /> </div> )
Чтобы упорядочить наш код, внутри приложения мы включим маршрутизатор только в Body, theReactResizeDetector
и эту новую функцию CheckIfRedirect
.
class App extends Component { render() { return ( <Router> <> <Body /> <ReactResizeDetector handleWidth handleHeight> <CheckIfRedirect /> </ReactResizeDetector> </> </Router> ); } }
В итоге полный код нашего приложения:
import React, { Component } from 'react' import Button from '@material-ui/core/Button' import Hidden from '@material-ui/core/Hidden' import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom" import Mobile from './Mobile' import Device from './Device' import ReactResizeDetector from 'react-resize-detector' const Body = () => ( <div className="App"> <Hidden xsDown> <Link to='/device'> <Button variant="contained" color="primary"> Show Device </Button> </Link> </Hidden> <Hidden smUp> <Link to='/mobile'> <Button variant="contained" color="secondary"> Show Mobile </Button> </Link> </Hidden> <Route path="/device" component={H1Device} /> <Route path="/mobile" component={H1Mobile} /> </div> ) const CheckIfRedirect = ({width, height}) => { const path = window.location.pathname console.log('resize', width, height, path) if ((width < 600 && path === "/device") || (width >= 600 && path === "/mobile")) { return <Redirect to={{pathname: "/"}} /> } else { return <></> } } class App extends Component { render() { return ( <Router> <> <Body /> <ReactResizeDetector handleWidth handleHeight> <CheckIfRedirect /> </ReactResizeDetector> </> </Router> ); } } export default App;
Конечный результат
На изображении ниже можно увидеть, как кнопка меняется с Устройство на Мобильное.
И нажатие на кнопку изменение пути.
Тестируем наше приложение
Очень важно написать тест, чтобы удостовериться, что все работает должным образом.
Я нашел информацию о том, как протестировать это приложение, в документации React-Testing-Examples. Еще одна библиотека, которая мне очень помогает, - это Библиотека тестирования DOM.
Установка…
npm install
react-testing-library
or
yarn add react-testing-library
Чтобы убедиться, что все идет хорошо, давайте приступим к созданию теста для рендеринга основного компонента.
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { render, waitForElement } from 'react-testing-library' const renderComponent = () => ( render( <App /> ) ) test('renders without crashing', async () => { const { getByText } = renderComponent(); await waitForElement(() => getByText('Show Device')); });
Теперь тестируем событие, чтобы щелкнуть по кнопке и отобразить сообщение о том, что браузер настроен на размер устройства.
Чтобы имитировать щелчок, мы добавляем fireEvent
, передавая сообщение, которое должно быть отображено в виде строки.
... import { render, waitForElement, fireEvent } from 'react-testing-library' ... test('click on show device', async () => { const { getByText } = renderComponent(); fireEvent.click(getByText('Show Device')); await waitForElement(() => getByText('I am rendered only on Device:)')); });
Теперь приступим к тонкой части нашего теста, чтобы смоделировать изменение размера экрана, изменив кнопку для мобильного устройства.
Для этого мы включаем функцию, используя global.innerWidth
в качестве стандартного размера для устройства (1024 пикселя) и добавляя global.dispachEvent
для отправки нового события изменения размера.
Тест для визуализации компонента и имитации щелчка такой же, как и раньше, для устройства, включая только новый размер экрана.
... afterEach(() => { global.innerWidth = 1024 global.dispatchEvent(new Event('resize')) }); test('click on show device', async () => { const { getByText } = renderComponent(); fireEvent.click(getByText('Show Device')); await waitForElement(() => getByText('I am rendered only on Device:)')); }); test('click on show mobile', async () => { // Change the viewport to 500px. global.innerWidth = 500; // Trigger the window resize event. global.dispatchEvent(new Event('resize')); const { getByText } = renderComponent(); fireEvent.click(getByText('Show Mobile')); await waitForElement(() => getByText('I am rendered only on mobile ;)')); }); test('resize to mobile', async () => { const { getByText } = renderComponent(); // Change the viewport to 500px. global.innerWidth = 500; // Trigger the window resize event. global.dispatchEvent(new Event('resize')); // wait react component render after event dispatch await waitForElement(() => getByText('Show Mobile')); fireEvent.click(getByText('Show Mobile')); await waitForElement(() => getByText('I am rendered only on mobile ;)')); });
В конце концов…
Это одно из решений для использования React Router и изменения пути в соответствии с изменением размера экрана, полезно в приложении, где необходимо изменить маршрут и отображаемый компонент, переходя с мобильного на устройство (или наоборот).
Я знаю, что существуют другие способы получить тот же конечный результат с использованием разных инструментов. А вы, как разработать свой код, чтобы создать что-то подобное? Для меня приятно получать разные мнения и технический опыт, чтобы делиться знаниями. :)
Вы можете прочитать этот пост на португальском здесь.