Привет! Это третья и заключительная часть серии статей “Лучшие подходы и решения для уменьшения кода на React” автора Rahul Sharma. Предыдущие статьи вы можете найти по ссылкам ниже:
Храните токен в куки, вместо localStorage
Плохой код:
const token = localStorage.getItem("token");
if (token) {
axios.defaults.headers.common["Authorization"] = token;
}
Хороший код:
import Cookies from "js-cookie"; // Вы можете использовать другую библиотеку
const token = Cookies.get("token");
if (token) {
axios.defaults.headers.common["Authorization"] = token;
}
Лучший код:
Без кода ????
Примечание:
Куки распространяются на все сайты внутри одного домена. Так что нет необходимости прописывать токен в каждом запросе. Если же бэкенд находится не на том же домене, что и фронтенд, то вам нужно использовать решение из следующего пункта.
Используйте HttpOnly атрибут для избегания получения доступа к куки через JavaScript.
Используйте перехватчики запросов (interceptors) для токена авторизации или других стандартных заголовков
Плохой код:
axios.get("/api", {
headers: {
ts: new Date().getTime(),
},
});
Хороший код:
axios.interceptors.request.use(
(config) => {
// Код, необходимый до отправки запроса
config.headers["ts"] = new Date().getTime();
return config;
},
(error) => {
// Обработка ошибки из запроса
return Promise.reject(error);
}
);
// Компонент
axios.get("/api");
Используйте react context/redux для передачи пропов в дочерние элементы
Плохой код:
const auth = { name: "John", age: 30 };
return (
<Router>
<Route path="/" element={<App auth={auth} />} />
<Route path="/home" element={<Home auth={auth} />} />
</Router>
);
Хороший код:
return (
<Provider store={store}>
<Router>
<Route
path="/"
element={<App />}
/>
<Route
path="/home"
element={<Home />}
/>
</Router>
);
// Внутри дочернего компонента
const { auth } = useContext(AuthContext); // При использовании контекста
const { auth } = useSelector((state) => state.auth); // При использовании redux
Используйте вспомогательные функции для styled-components
Не плохой код, но сложный для чтения, так как все привыкли думать в пикселях:
const Button = styled.button`
margin: 1.31rem 1.43rem;
padding: 1.25rem 1.5rem;
`;
Создайте вспомогательную функцию для перевода пикселей в rem:
const toRem = (value) => `${value / 16}rem`;
const Button = styled.button`
margin: ${toRem(21)} ${toRem(23)};
padding: ${toRem(20)} ${toRem(24)};
`;
Используйте универсальные функции для изменения данных внутри полей ввода
Плохой код:
const onNameChange = (e) => setName(e.target.value);
const onEmailChange = (e) => setEmail(e.target.value);
return (
<form>
<input type="text" name="name" onChange={onNameChange} />
<input type="text" name="email" onChange={onEmailChange} />
</form>
);
Хороший код:
const onInputChange = (e) => {
const { name, value } = e.target;
setFormData((prevState) => ({
...prevState,
[name]: value,
}));
};
return (
<form>
<input type="text" name="name" onChange={onInputChange} />
<input type="text" name="email" onChange={onInputChange} />
</form>
);
Используйте intersection observer для реализации отложенной загрузки
Плохой код:
element.addEventListener("scroll", function (e) {
// do something
});
Хороший код:
const useScroll = (ele, options = {}): boolean => {
const [isIntersecting, setIsIntersecting] = useState(false);
useEffect(() => {
const cb = (entry) => setIsIntersecting(() => entry.isIntersecting);
const callback: IntersectionObserverCallback = (entries) => entries.forEach(cb);
const observer = new IntersectionObserver(callback, options);
if (ele) observer.observe(ele);
return (): void => ele && observer.unobserve(ele);
}, [ele]);
return isIntersecting;
};
// Компонент
const ref = useRef<any>();
const isIntersecting = useScroll(ref?.current);
useEffect(() => {
if (isIntersecting) {
// вызов API
}
}, [isIntersecting]);
Используйте HOC для авторизации и приватной навигации
Плохой код:
const Component = () => {
if (!isAuthenticated()) {
return <Redirect to="/login" />;
}
return <div></div>;
};
Хороший код:
const withAuth = (Component) => {
return (props) => {
if (!isAuthenticated()) {
return <Redirect to="/login" />;
}
return <Component {...props} />;
};
};
// Навигация
<Route path="/home" component={withAuth(Home)} />;
// Компонент
const Component = (props) => <div></div>;
export default withAuth(Component);
Используйте массив с объектами данных навигации при ее создании
Стандартное решение:
return (
<Router>
<Route path="/" element={<App />} />
<Route path="/about" element={<About />} />
<Route path="/topics" element={<Topics />} />
</Router>
);
Хороший код:
const routes = [
{
path: "/",
role: ["ADMIN"],
element: React.lazy(() => import("../pages/App")),
children: [
{
path: "/child",
element: React.lazy(() => import("../pages/Child")),
},
],
},
{
path: "/about",
role: [],
element: React.lazy(() => import("../pages/About")),
},
{
path: "/topics",
role: ["User"],
element: React.lazy(() => import("../pages/Topics")),
},
];
const createRoute = ({ element, children, role, ...route }) => {
const Component = role.length > 0 ? withAuth(element) : element;
return (
<Route key={route.path} {...route} element={<Component />}>
{children && children.map(createRoute)}
</Route>
);
};
return <Routes>{routes.map(createRoute)}</Routes>;
Примечание: это требует больше кода, но становится более настраиваемым. К примеру, если вы хотите использовать больше HOC, вам нужно всего лишь обновить createRoute.
Используйте Typescript
Ничего плохого, если вы не используете Typescript ????, попробуйте он поможет сделать ваш код лучше.
npx create-react-app my-app --template typescript
Используйте eslint, prettier для форматирования
npm install -D eslint prettier
npx eslint --init
Ссылки для помощи в настройке: Eslint, Prettier
Используйте pre-commit хук для запуска eslint и prettier
Плохой код:
npx mrm@2 lint-staged // Установка и конфигурация pre-commit хука
// Скрипт будет создан в корне вашего проекта
.husky/pre-commit
// Package.json
"lint-staged": {
"src/**/*.{js,ts,jsx,tsx}": [
"npm run lint",
"npm run prettier",
"npm run unit-test",
"git add"
]
}
Примечание:
Вы можете обновить конфиг для запуска prettier и eslint при коммите. Вы также можете добавлять или удалять команды в package.json.
Лучше, если в проекте настроен CI & CD для этого. Кто-то может закомментировать pre-commit хук и запушить его в гит.
Несколько VSCode расширений для более легкой разработки
Auto Close Tag, Auto Rename Tag, CodeMetrics, CSS Peek, ES7+ React/Redux/React-Native snippets, Eslint, GitLens, Import Cost, Prettier
Примечание: попробуйте расширения для оценки сложности функций (CodeMetrics). Это поможет в написании более хорошего кода, так как вы будете видеть сложность выполнения кода.
Спасибо за прочтение ????
superman_cherry
Используйте react context/redux для передачи пропов в дочерние элементы
Добрый день, проверяли каждый из подходов на предмет производительности?
Что будет менее затратно: передавать в пропсах или в каждом children дёргать hook?