Это вторая статья в серии, охватывающей различные стратегии для упрощения ваших приложений React.
Упростите компонент React
Есть ряд стратегий, которые мы можем принять, чтобы упростить наши компоненты без капризации существующих компонентов. Каждая стратегия будет рассмотрена в другом блоге.
- Отдельное состояние от дисплея, это поможет вашему приложению соответствовать хорошо установленным правилам MVC
- Отложить обработку на услуги и пользовательские крючки
- Избегайте перегрузки
ИспользоватьиUSESTATE - Определите, если
ReduxиRedux-sagaдействительно нужны - Создать компоненты более высокого порядка для объединения функциональности между компонентами
- Shift Computational Logic Out из компонентов в вспомогательные функции, внедрение с помощью пользовательских крючков
- Используйте ленивую нагрузку и ленивое поведение, где это возможно
Отложить обработку на услуги и пользовательские крючки
React не хватает концепции услуг, в отличие от Angular, которая запекает в рамках, особенно с помощью инъекционных ресурсов. Как разработчики React, нам нужно выпекать эту функциональность в наших проектах. Услуги аккуратно инкапсулируют конкретную функциональность, становятся взаимозаменяемым ресурсом с помощью литья TypeScript и является еще одним шагом к смещению логики от уровня презентации.
Довольно часто мы увидим компонент, который может сделать вывоз и презентацию в том же компоненте:
const myComponent: React.FC = () => {
const [ todos, setTodos ] = useState([]);
useEffect(async () => {
const result = await axios.get("https://jsonplaceholder.typicode.com/todos");
const todos = res.data.filter(!!todos.completed));
setTodos(todos);
});
return (
{ todos.map(item => (
-
{item.title}
))}
)
}
Уровень поверхности это не похоже на то, что с этим компонентом не так много. Но что, если нам нужно начать обрабатывать ошибки из API, дополнительные проверки и манипуляции с данными? Наш Использоватьэффект Крюк внезапно становится перегруженным, раздутым поведением, которое может и должно быть отложено на услугу.
useEffect(async () => {
try {
const result = await axios.get("https://jsonplaceholder.typicode.com/todos");
const todos = res.data.filter(!!todos.completed));
setTodos(todos);
} catch (e) {
setLoaded(false);
setErrorMessage("Could not load todos, please refresh your browser and make sure you're connected to the internet!");
}
});
Чем больше мы добавляем к нашим Использоватьэффект Крюк, тем сложнее становится компонент, и тем сложнее он станет тестировать. Отложенный/асинхронный рендеринг уже затрудняет тестирование, с такими инструментами, как обновления Jest и Enzyme, но нелегко.
Мы можем упростить этот код, переведя обработку API на службу, позволяя нам последовательно выполнять запросы и последовательно обрабатывать ошибки, и разделить Использоватьэффект код в пользовательский крючок.
type Todo = { id: number, title: string };
type TodosService = {
todos: async (completed?: boolean) => Promise>,
todo: async (id: number) => Promise
};
class TodosServiceImpl implements TodosService {
async todos(completed?: boolean): Promise> {
try {
const result = await axios.get("https://jsonplaceholder.typicode.com/todos");
if (completed !== undefined) {
return res.data.filter(todo => todo.completed === completed));
}
return res.data;
} catch (e) {
throw "Could not load todos, please refresh your browser and make sure you're connected to the internet!";
}
}
async todo(id: number): Promise {
try {
const result = await axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`);
return res.data;
} catch (e) {
throw `Could not load todo ${id}, please refresh your browser and make sure you're connected to the internet!`;
}
}
}
В случае, если нам нужна взаимозаменяемая услуга, мы можем предоставить новую услугу, если она удовлетворяет контракту Todosservice :
const todosService: TodosService = {
todos: async (completed?: boolean): Promise> => {...}
todo: async (id: number): Promise => {...}
}
// test of the implementation
Теперь, когда у нас есть внедрение услуг, мы можем потреблять ее в наших компонентах:
const todosService: TodosService = new TodosServiceImpl();
const useTodosLoader = (todosService: TodosService) => {
const [ todos, setTodos ] = useState>([]);
const [ hasError, setHasError ] = useState(false);
const [ loaded, setLoaded ] = useState(false);
useEffect(async () => {
try {
const list = await todosService.todos();
setTodos(list);
setLoaded(true);
} catch (e) {
setHasError(true);
}
}, []);
return { todos, hasError, loaded };
}
const myComponent: React.FC<{ todosService: TodosService }> = ({ todosService }) => {
const { todos, hasError, loaded } = useTodosLoaded(todosService);
return (
{ todos.map(item => (
-
{item.title}
))}
)
}
Все аспекты приведенного выше кода тестируемы – мы можем убедиться, что сервис называется, мы также можем проверить, что API называется. Мы можем проверить загрузку MyComponent Через ответы USETODOLOADER , и мы можем издеваться над поведением правильно. Несмотря на то, что мы, по крайней мере, удвоили объем кода для упрощения компонента, увеличение кода напрямую пропорционально простоте функционального кода и тестового кода.
Пользовательские крючки позволяют нам логически группировать поведение вместе, особенно когда мы манипуплируем состояние, используя USESTATE крючки Мы можем разоблачить выходы из крючка, которые будут употреблять в нашем компоненте, что позволяет обновлять, когда крючки меняют состояние. Это обеспечивает богатую функциональность для использования перекрестных компонентов, особенно при использовании USESTATE Поддерживать состояние между компонентами.
const useMyState = () => {
const [ myState, setMyState ] = useState(true);
return { myState, setMyState }
}
const myComponent = () => {
const { myState } = useMyState();
...
}
const myOtherComponent = () => {
const { myState, setMyState } = useMyState();
useEffect(() => {
setTimeout(() => {setMyState(false)});
}, []);
...
}
Мы также можем использовать крючки, чтобы подписаться на потоки событий. Это позволяет Mutliple не подключенные компоненты одновременно обновляться в зависимости от изменений состояния или движущихся событиями.
const myEventStream = () => {
const [ myState, setMyState ] = useState(null);
useEffect(() => {
const subscription = observable.subscribe();
subscription.next(event => setMyState(event.data));
return () => subscription.unsubscribe();
})
}
const myComponent = () => {
const { myState } = useMyState();
...
}
const myOtherComponent = () => {
const { myState } = useMyState();
...
}
observable.next({data: { foo: "bar"}});
// Updates myComponent
// Updates myOtherComponent
Следите за новостями, когда мы посмотрим на перегрузку Использоватьэффект и USESTATE Анкет
Оригинал: “https://dev.to/jmitchell38488/less-is-more-simplify-your-react-code-to-super-power-your-applications-part-2-4jam”