Компоненты React
Пример функционального компонента:
function App() {
return (
<div>Hello world!</div>
);
}
Реактивные реквизиты
Данные, передаваемые от главного компонента дочернему компоненту, называются реквизитами. Реквизиты (свойства) — это неизменяемые компоненты только для чтения, и в качестве реквизита может быть передано любое значение javascript, включая компоненты.
Пример использования реквизита для компонента функции:
function App() {
return <User name="John Doe" />
}
function User(props) {
return <h1>Hello, {props.name}</h1>; // Hello, John Doe!
}
Использование prop с методом деструктуризации объекта:
function App() {
return <User name="John Doe" />
}
function User({ name }) {
return <h1>Hello, {name}!</h1>; // Hello, John Doe!
}
Реактивные дети Реквизиты
Реквизиты также можно передавать, помещая данные между открывающим и закрывающим тегами компонента. Реквизиты, передаваемые таким образом, называются дочерними реквизитами и позволяют вложить JSX и передать его другим компонентам:
function App() {
return (
<User>
<h1>Hello, John Doe!</h1>
</User>
);
}
function User({ children }) {
return children; //<h1>Hello, John Doe!</h1>
: Hello, John Doe!
}
React Conditionals
В React условие if не используется в jsx, вместо него мы используем троичный оператор.
function App() {
const isAuthUser = useAuth();
return (
<>
<h1>My App</h1>
{isAuthUser ? <AuthApp /> : <UnAuthApp />}
</>
)
}
Списки React
.map() fonksiyonu
.map() позволяет нам перебирать массивы данных и выводить JSX.
function SoccerPlayers() {
const players = ["Messi", "Ronaldo", "Laspada"];
return (
<div>
{players.map((playerName) => (
<SoccerPlayer key={playerName} name={playerName} />
))}
</div>
);
}
Когда вы возвращаете список в jsx, не забудьте добавить к нему уникальное ключевое значение. Значения ключей должны быть уникальными только между братьями и сестрами.
React Context
Контекст React позволяет нам передавать данные в дерево компонентов без использования props.
Проблема с реквизитами заключается в том, что иногда мы передаем их компонентам, которые не должны их получать. Эта проблема называется бурение пропсов
.
В приведенном ниже примере реквизит передается компоненту Body (хотя компонент body не использует этот реквизит), чтобы передать его своему подкомпоненту, компоненту Greeting.
function App() {
return (
<Body name="John Doe" />
);
}
function Body({ name }) {
return (
<Greeting name={name} />
);
}
function Greeting({ name }) {
return <h1>Welcome, {name}</h1>;
}
Чтобы использовать контекст, мы используем функцию createContext из React.
Мы задаем контекст с начальным значением, которое будет помещено в контекст.
Созданный контекст содержит свойства Provider<###code> и
Consumer<###code> (Provider и Consumer являются компонентами).
Мы оборачиваем Provider вокруг дерева компонентов, которому хотим передать заданное значение. Затем мы помещаем Comsumer в компонент, где мы хотим потреблять значение.
import { createContext } from 'react';
const NameContext = createContext('');
function App() {
return (
<NameContext.Provider value="John Doe">
<Body />
<NameContext.Provider>
);
}
function Body() {
return <Greeting />;
}
function Greeting() {
return (
<NameContext.Consumer>
{name => <h1>Welcome, {name}</h1>}
</NameContext.Consumer>
);
}
Реактивные крючки
Он был введен в React и обеспечивает простой способ добавления информации о состоянии в функциональные компоненты.
Вы можете использовать существующие хуки или написать собственные хуки, если вы хотите обеспечить специальную функциональность вашего приложения.
Hooks не работают внутри классов и позволяют использовать React без классов.
Правила работы с крючками
Всегда используйте Hooks на верхнем уровне вашей функции React, перед любым возвратом. Следуя этому правилу, вы гарантируете, что крючки будут вызываться в одном и том же порядке каждый раз при рендеринге компонента. Именно это позволяет React корректно поддерживать состояние крючков между несколькими вызовами useState и useEffect.
Не вызывайте крючки внутри циклов, условий или вложенных функций.
Вызывайте Hooks только в функциональных компонентах React. Не вызывайте Hooks внутри обычных функций JavaScript.
⚡️useState Hook
Функция useState
позволяет использовать в компонентах значения с состоянием.
import { useState } from 'react';
function MyComponent() {
const [stateValue, setStateValue] = useState(initialValue);
}
Основной пример использования UseState - инкрементирование счетчика.
Мы можем узнать текущий счет из переменной count и увеличить состояние, передав count + 1 в функцию setCount.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function updateCount() {
setCount(count + 1);
}
return <button onClick={updateCount}>Count is: {count}</button>;
}
Инициализация состояния из функции
const StateFromFn = () => {
const [token] = useState(() => {
let token = window.localStorage.getItem("my-token");
return token || "default#-token#"
})
return <div>Token is {token}</div>
}
Предыдущее значение состояния prevState
const CounterFnSetState = () => {
const [count, setCount] = useState(0);
return (
<>
<p>Count value is: {count}</p>
<button onClick={() => setCount(0)}>Reset</button>
<button
onClick={() => setCount(prevCount => prevCount + 1)}>
Plus (+)
</button>
<button
onClick={() => setCount(prevCount => prevCount - 1)}>
Minus (-)
</button>
</>
);
}
⚡️ использованиеЭффект крючка
Если мы хотим взаимодействовать с внешним миром, например, использовать API, мы используем хук useEffect.
useEffect используется для выполнения побочного эффекта, что означает выполнение операции, которая существует вне нашего приложения и не имеет предсказуемого результата.
React выполняет эффекты после каждого рендеринга. Это включает в себя первый рендеринг.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// perform side effect here
}, []);
}
Если мы хотим получить данные, мы используем useEffect, например, для получения и отображения списка постов:
import { useEffect } from 'react';
function PostList() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(posts => setPosts(posts));
}, []);
return posts.map(post => <Post key={post.id} post={post} />
}
Если нам нужно использовать значение вне функции эффекта, оно должно быть включено в массив зависимостей.
Например, фрагмент кода, который добавляет или удаляет класс "overflow-hidden" для элемента body каждый раз, когда мобильное меню открывается или закрывается.
function Mobile({ open }) {
useEffect(() => {
const body = document.querySelector("#__next");
if (open) {
body.classList.add("overflow-hidden");
} else {
body.classList.remove("overflow-hidden");
}
}, [open]);
}
Оптимизация производительности за счет обхода эффектов
Вы можете сказать React пропустить применение эффекта, если определенные значения не изменились между повторными рендерингами. Для этого передайте массив в качестве необязательного второго параметра в useEffect:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
В приведенном выше примере мы передаем [count] в качестве второго аргумента. Это означает, что если count равен 5, а затем наш компонент рендерится снова с count по-прежнему равным 5, React сравнивает [5] в предыдущем рендеринге с [5] в следующем рендеринге. Поскольку все элементы в массиве одинаковы (5 === 5), React пропускает этот эффект.
Если вы хотите запустить эффект и очистить его только один раз (во время монтирования и размонтирования), вы можете передать пустой массив ([]) в качестве второго аргумента. Это говорит React, что ваш эффект не привязан к какому-либо значению в props или state, поэтому его никогда не нужно запускать снова.
Пропуск эффектов (пустой массив зависимостей)
Эффект вызывается только во время монтажа (в случае, если компонент отрисовывается первым).
useEffect(() => {
document.title = `New value: ${value}`
},[])
Пропуск эффектов (Зависимость (без использования массива зависимостей))
В этом случае функция вызывается снова после каждого рендеринга. Например, каждый раз, когда состояние обновляется, запускается и функция useEffect.
useEffect(() => {
console.log(“This will be logged after every render!”)
})
Условное использованиеЭффект
Если мы хотим запустить эффект условно, мы можем поместить это условие в наш Hook:
useEffect(() => {
if (value > 0) {
document.title = `New value: ${value}`
}
})
функция очистки useEffect
Чтобы выполнить код при удалении или уничтожении компонента, необходимо добавить оператор 'return' в функцию useEffect.
useEffect(() => {
const timer = window.setInterval(() => {
setCount(count => count + 1)
}, 1000)
return () => clearInterval(timer)
}, [])
Код 'clearInterval(timer)' выполняется только перед удалением компонента из пользовательского интерфейса.
другой пример:
const EffectCleanup = () => {
useEffect(() => {
const clicked = () => console.log('window clicked')
window.addEventListener('click', clicked)
// return a clean-up function
return () => {
window.removeEventListener('click', clicked)
}
}, [])
return <div>
When you click the window you'll
find a message logged to the console
</div>
}
Множественные вставки if
<span className={count === 0 && 'text-gray-500' || count > 0 && 'text-green-500' || count < 0 && 'text-red-500'}>{count}</span>
⚡️useRef Hook
useRef
позволяет нам получить прямой доступ к элементу JSX.
useRef в основном используется для нацеливания на элемент DOM. Однако он также используется для поддержания значения, которое меняется между каждым рендерингом. useRef не вызывает повторного рендеринга, как useState.
import { useRef } from 'react';
function MyComponent() {
const ref = useRef();
return <div ref={ref} />
}
Когда ссылка добавляется к элементу, мы можем использовать значение, хранящееся в ref.current
, для доступа к самому элементу.
Например, если мы хотим написать код, который фокусируется на поисковом импульсе, когда пользователи используют комбинацию клавиш Control + K:
import { useWindowEvent } from "@mantine/hooks";
import { useRef } from "react";
function Header() {
const inputRef = useRef();
useWindowEvent("keydown", (event) => {
if (event.code === "KeyK" && event.ctrlKey) {
event.preventDefault();
inputRef.current.focus();
}
});
return <input ref={inputRef} />
}
Другой пример:
const UseRefBasics = () => {
const refContainer = useRef(null)
const handleSubmit = (e) => {
e.preventDefault()
console.log(refContainer.current.value)
}
useEffect(() => {
refContainer.current.focus()
}, [])
return (
<div>
<form className="form" onSubmit={handleSubmit}>
<div>
<input ref={refContainer} type="text" />
<button type="submit">Submit</button>
</div>
</form>
</div>
)
};
⚡️useContext Hook
useContext обеспечивает более простой способ потребления контекста, чем использование стандартного компонента Context.Consumer.
Синтаксис включает передачу всего объекта Context, который мы хотим использовать, в useContext. Возвращаемое значение - это значение, переданное в Context.
import { useContext } from 'react';
function MyComponent() {
const value = useContext(Context);
}
Перепишем наш предыдущий пример с использованием хука useContext:
import { createContext, useContext } from 'react';
const NameContext = createContext('');
function App() {
return (
<NameContext.Provider value="John Doe">
<Body />
</NameContext.Provider>
);
}
function Body() {
return <Greeting />;
}
function Greeting() {
const name = useContext(NameContext);
return (
<h1>Welcome, {name}</h1>
);
}
Примечание: чтобы вызвать созданный вами контекст с другой страницы, не забудьте добавить export к ключевому слову const! 🙂
Пример: export const name = useContext(NameContext);
Другой пример:
// example Context object
const ThemeContext = React.createContext("dark");
// usage with context Consumer
function Button() {
return <ThemeContext.Consumer>
{theme => <button className={theme}> Amazing button </button>}
</ThemeContext.Consumer>
}
// usage with useContext hook
import {useContext} from 'react';
function ButtonHooks() {
const theme = useContext(ThemeContext)
return <button className={theme}>Amazing button</button>
}
Чтобы использовать значение:
const ThemeContext = React.createContext('light');
const Display = () => {
const theme = useContext(ThemeContext);
return <div
style={{
background: theme === 'dark' ? 'black' : 'papayawhip',
color: theme === 'dark' ? 'white' : 'palevioletred',
width: '100%',
minHeight: '200px'
}}
>
{'The theme here is ' + theme}
</div>
}
⚡️useCallback Hook
useCallback
- это хук, который мы используем для повышения производительности нашего приложения.
В частности, это предотвращает повторное отображение функций при каждом рендеринге компонента (повторном рендеринге), что может снизить производительность нашего приложения.
Чтобы использовать хук, мы обернем нашу функцию обратного вызова в useCallback и добавим в список зависимостей параметр, который заставит повторно запустить функцию, если он изменится.
function App() {
const [player, setPlayer] = React.useState("");
const [players, setPlayers] = React.useState(["Messi", "Ronaldo", "Laspada"]);
function handleChangeInput(event) {
setPlayer(event.target.value);
}
function handleAddPlayer() {
setPlayers(players.concat(player));
}
const handleRemovePlayer = useCallback(player => {
setPlayers(players.filter((p) => p !== player));
}, [players])
return (
<>
<input onChange={handleChangeInput} />
<button onClick={handleAddPlayer}>Add Player</button>
<PlayerList players={players} handleRemovePlayer={handleRemovePlayer} />
</>
);
}
function PlayerList({ players, handleRemovePlayer }) {
return (
<ul>
{players.map((player) => (
<li key={player} onClick={() => handleRemovePlayer(player)}>
{player}
</li>
))}
</ul>
);
}
⚡️ UseMemo Hook
useMemo - это еще один крючок производительности, который позволяет нам "запомнить" определенную операцию.
Мемоизация позволяет запомнить результат дорогостоящих вычислений, когда они уже выполнены, чтобы не пересчитывать их заново. (Когда вы запускаете функцию с теми же переменными, она не пересчитывается и не перерисовывается, а использует значение вычисления, выполненного ранее).
Как и useEffect и useCallback, useMemo принимает функцию обратного вызова и массив зависимостей.
Однако, в отличие от обеих этих функций, функция useMemo должна возвращать значение.
Вы должны вернуть значение либо явно с помощью ключевого слова return, либо неявно, используя ярлык функции arrow.
function App() {
const [count, setCount] = useState(10)
const expensiveComputation = useMemo(() => {
return count * 2
}, [count])
return (
<>
<p>Count: {count}</p>
<p>Expensive count: {expensiveComputation}</p>
<button onClick={() => setCount(count + 1)}>Increment count</button>
</>
)
}
Реальный пример UseMemo взят из документации по mdx-bundler. mdx-bundler - это библиотека для преобразования файлов .mdx в компоненты React.
Здесь используется useMemo для преобразования последовательности необработанного кода в компонент React.
import * as React from 'react'
import {getMDXComponent} from 'mdx-bundler/client'
function Post({code, frontmatter}) {
const Component = React.useMemo(() => getMDXComponent(code), [code]);
return (
<>
<header>
<h1>{frontmatter.title}</h1>
<p>{frontmatter.description}</p>
</header>
<main>
<Component />
</main>
</>
)
}
Это делается для того, чтобы избежать ненужного перестроения значения компонента при его восстановлении.
Поэтому useMemo выполняет функцию обратного вызова только при изменении зависимости кода.
В приведенном ниже примере объект someValue можно отметить с помощью useMemo. Это позволяет избежать ненужного повторного рендеринга.
const App = () => {
const [age, setAge] = useState(99)
const handleClick = () => setAge(age + 1)
const someValue = useMemo(() => ({ value: "someValue" }))
const doSomething = () => {
return someValue
}
return (
<div>
<Age age={age} handleClick={handleClick}/>
<Instructions doSomething={doSomething} />
</div>
)
}
const Age = ({ age, handleClick }) => {
return (
<div>
<div style={{ border: '2px', background: "papayawhip", padding: "1rem" }}>
Today I am {age} Years of Age
</div>
<pre> - click the button below 👇 </pre>
<button onClick={handleClick}>Get older! </button>
</div>
)
}
const Instructions = React.memo((props) => {
return (
<div style={{ background: 'black', color: 'yellow', padding: "1rem" }}>
<p>Follow the instructions above as closely as possible</p>
</div>
)
})
ReactDOM.render (<App />)
⚡️ используйте редукционный крючок
function App() {
function reducer(state, action) {
switch (action.type) {
case 'plus':
return state + 1
case 'minus':
return state - 1
}
}
const [count, dispatch] = useReducer(reducer, 0)
return (
<>
Count: {count}
<button onClick={() => dispatch({ type: 'plus' })}>Plus 1</button>
<button onClick={() => dispatch({ type: 'minus' })}>Minus 1</button>
</>
)
}
⚡️useLayoutEffect Hook
Его использование аналогично использованию UseEffect.
const ArrayDep = () => {
const [randomNumber, setRandomNumber] = useState(0)
const [effectLogs, setEffectLogs] = useState([])
useLayoutEffect(
() => {
setEffectLogs(prevEffectLogs => [...prevEffectLogs, 'effect fn has been invoked'])
},
[randomNumber]
)
return (
<div>
<h1>{randomNumber}</h1>
<button
onClick={() => {
setRandomNumber(Math.random())
}}
>
Generate random number!
</button>
<div>
{effectLogs.map((effect, index) => (
<div key={index}>{'🍔'.repeat(index) + effect}</div>
))}
</div>
</div>
)
}
В чем разница между useEffect и useLayoutEffect? Функция, переданная в useEffect, активируется после того, как рендеринг был выведен на экран.
Это удобно для большинства побочных эффектов, которые не должны мешать браузеру обновлять экран.
Однако бывают ситуации, когда вам не нужно поведение, которое обеспечивает useEffect; например, если вам нужно внести визуальные изменения в DOM в качестве побочного эффекта, useEffect - не лучший выбор.
Вы можете использовать функцию useLayoutEffect, чтобы пользователь не видел мерцания изменений. Функция, переданная в useLayoutEffect, будет выполнена до того, как браузер обновит экран.
⚡️ используйте редукторный крючок
useReducer может быть использован как альтернатива useState. Он идеально подходит для сложной логики состояний, где есть зависимость от предыдущего значения состояния или многих подзначений состояния.
const initialState = { width: 15 };
const reducer = (state, action) => {
switch (action) {
case 'plus':
return { width: state.width + 15 }
case 'minus':
return { width: Math.max(state.width - 15, 2) }
default:
throw new Error("what's going on?" )
}
}
const Bar = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return <>
<div style={{ background: 'teal', height: '30px', width: state.width }}></div>
<div style={{marginTop: '3rem'}}>
<button onClick={() => dispatch('plus')}>Increase bar size</button>
<button onClick={() => dispatch('minus')}>Decrease bar size</button>
</div>
</>
}
ReactDOM.render(<Bar />)
Лениво инициализируйте состояние
useReducer принимает третий параметр функции. Вы можете инициализировать состояние из этой функции, и все, что возвращается из этой функции, возвращается как объект состояния.
const initializeState = () => ({
width: 100
})
const initialState = { width: 15 }
const reducer = (state, action) => {
switch (action) {
case 'plus':
return { width: state.width + 15 }
case 'minus':
return { width: Math.max(state.width - 15, 2) }
default:
throw new Error("what's going on?" )
}
}
const Bar = () => {
const [state, dispatch] = useReducer(reducer, initialState, initializeState)
return <>
<div style={{ background: 'teal', height: '30px', width: state.width }}></div>
<div style={{marginTop: '3rem'}}>
<button onClick={() => dispatch('plus')}>Increase bar size</button>
<button onClick={() => dispatch('minus')}>Decrease bar size</button>
</div>
</>
}
ReactDOM.render(Bar)
e.preventDefault()
В React функция e.preventDefault() используется для предотвращения поведения элемента по умолчанию.
Пример: Чтобы предотвратить событие onSubmit формы:
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
React.lazy
Функция React.lazy используется для рендеринга динамического импорта как обычного компонента.
const OtherComponent = React.lazy(() => import('./OtherComponent'));
Саспенс
Саспенс позволяет компонентам "ждать" чего-то, прежде чем это что-то будет сделано. В настоящее время Suspense поддерживает только один вариант использования: динамическая загрузка компонентов с помощью React.lazy.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
//fallback prop’u, bileşenin
yüklenmesini beklerken göstermek
istediğiniz herhangi bir React elemanını
kabul eder.
<Suspense fallback={<div>Yükleniyor...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
Переходы
Transitions позволяет пометить обновления как переходы, что сообщает React, что они могут быть прерваны, и позволяет избежать возврата к резервным копиям Suspense для контента, который уже виден.
useTransition
const [isPending, startTransition] = useTransition();
Возвращает значение состояния ожидания перехода и функцию для его запуска.
startTransition позволяет отметить обновления в предоставленном обратном вызове как переходы:
startTransition(() => {
setCount(count + 1);
})
isPending указывает, когда переход активен, чтобы показать состояние ожидания:
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
})
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
Значение реквизита по умолчанию
const Person = ({name, age, children}) => {
return (
<h1>Name: {name} Age: {age}</h1>
<p>{children}</p>
)
}
Person.defaultProps = {
name: 'No name',
age: 0,
}
Деструктуризация объектов реквизита
function App(){
return people.map(person => <Person key={person.id} {...person} />)
}
const Person = ({name, age}) => {
return (
<h1>Name: {name}, Age: {age}</h1>
)
}
Будет продолжать обновляться ...
Источники 🙂
- https://tr.reactjs.org/docs/introducing-jsx.html
- https://www.freecodecamp.org/news/the-react-cheatsheet/
- https://medium.com/coinmonks/react-js-cheatsheet-for-beginners-5665425d9026
- https://stackoverflow.com/questions/65823965/react-context-is-not-defined-no-undef/65824439#65824439
- https://dev.to/ericchapman/react-cheat-sheet-updated-may-2021-1mcd
- https://codingcheats.io/react/
- https://blog.logrocket.com/react-hooks-cheat-sheet-unlock-solutions-to-common-problems-af4caf699e70/