Каталог статей:
- Что такое Render Props
- Применение Render Props
- Что такое React-хуки
- Применение хуков React
- Суммировать
Что такое Render Props
Короче говоря, пока значение свойства в компоненте является функцией, можно сказать, что компонент использует Render Props. Похоже, это так. Итак, каковы сценарии применения Render Props? Давайте начнем с простого примера. Если мы хотим реализовать компонент приветствия, это может быть реализовано в начале следующим образом:
const Greeting = props => (
<div>
<h1>{props.text}</h1>
</div>
);
// 然后这么使用
<Greeting text="Hello 🌰!" />
Но если вам также нужно отправить выражение при приветствии, то это может быть реализовано так:
const Greeting = props => (
<div>
<h1>{props.text}</h1>
<p>{props.emoji}</p>
</div>
);
// how to use
<Greeting text="Hello 🌰!" emoji="😳" />
Затем, если вы хотите добавить ссылку, вам нужноGreeting
Логика отправки ссылок реализована внутри компонента, что явно нарушает один из шести принципов разработки ПО.принцип открыто-закрыто, то есть каждую модификацию нужно модифицировать внутри компонента.
Принцип открыт-закрыт: закрыт для модификации, открыт для расширения.
Есть ли способ избежать такой модификации?Конечно, есть, о чем мы поговорим далее.Render Props, но перед этим рассмотрим очень простую функцию суммирования:
const sumOf = array => {
const sum = array.reduce((prev, current) => {
prev += current;
return prev;
}, 0);
console.log(sum);
}
Функция этой функции очень проста, просуммируйте массив и распечатайте его. Но если вам нужно поставитьsum
пройти черезalert
Покажи, он снова появится?sumOf
Чтобы изменить его внутренне, и вышеGreeting
Аналогично, да, у этих двух функций одна и та же проблема, то есть при изменении требований их обе нужно модифицировать внутри функции.
Что касается второй функции, вы, возможно, скоро сможете понять, как ее использовать.Перезвонитерешать:
const sumOf = (array, done) => {
const sum = array.reduce((prev, current) => {
prev += current;
return prev;
}, 0);
done(sum);
}
sumOf([1, 2, 3], sum => {
console.log(sum);
// or
alert(sum);
})
Вы обнаружите, что функция обратного вызова прекрасно решает предыдущие проблемы, каждый раз, когда мы ее модифицируем, нам нужно толькоsumOf
Измените его в функции обратного вызова функции, не переходя кsumOf
Измените его внутренне.
В отличие от компонентов ReactGreeting
, решить возникшие ранее проблемы, по сути, иsumOf
Функция обратного вызова аналогична:
const Greeting = props => {
return props.render(props);
};
// how to use
<Greeting
text="Hello 🌰!"
emoji="😳"
link="link here"
render={(props) => (
<div>
<h1>{props.text}</h1>
<p>{props.emoji}</p>
<a href={props.link}></a>
</div>
)}></Greeting>
аналогия с предыдущимsumOf
Это очень похоже, только волосок такой же:
-
sumOf
выполнив функцию обратного вызова вdone
и положиsum
в него, покаsumOf
Передайте функцию во втором параметре функции, чтобы получитьsum
значение, а затем напишите индивидуальные требования -
Greeting
выполнив функцию обратного вызова вprops.render
и положиprops
в него, покаGreeting
компонентrender
Вы можете получить функцию, передав функциюprops
значение и вернуть желаемый пользовательский интерфейс
Стоит отметить, что не толькоrender
Реквизиты рендеринга можно вызывать только путем передачи функции в атрибут. На самом деле любой атрибут может называться реквизитами рендеринга, если его значение является функцией. Например, в приведенном выше примереrender
имя свойства изменено наchildren
Это на самом деле проще в использовании:
const Greeting = props => {
return props.children(props);
};
// how to use
<Greeting text="Hello 🌰!" emoji="😳" link="link here">
{(props) => (
<div>
<h1>{props.text}</h1>
<p>{props.emoji}</p>
<a href={props.link}></a>
</div>
)}
</Greeting>
Таким образом, вы можете напрямуюGreeting
Функция написана в теге, по сравнению с предыдущейrender
более интуитивным.
так,Render Props в React Вы можете думать об этом как о функции обратного вызова в JavaScript..
Применение Render Props
Вышеизложенное кратко описывает, что такое Render Props, а затем каково практическое применение Render Props в реальной разработке?компоненты более высокого порядкаРешаемые задачи аналогичны, как дляРешить проблему повторного использования кода.
Если вы не знакомы с высокоуровневыми компонентами, то можете прочитать, что автор писал ранееКомпоненты высшего порядка в React и сценарии их применения.
Компонент, который просто реализует функцию «переключателя»:
class Switch extends React.Component {
constructor(props) {
super(props);
this.state = {
on: props.initialState || false,
};
}
toggle() {
this.setState({
on: !this.state.on,
});
}
render() {
return (
<div>{this.props.children({
on,
toggle: this.toggle,
})}</div>
);
}
}
// how to use
const App = () => (
<Switch initialState={false}>{({on, toggle}) => {
<Button onClick={toggle}>Show Modal</Button>
<Modal visible={on} onSure={toggle}></Modal>
}}</Switch>
);
это простоПовторное использование отображения и логики скрытых модальных всплывающих оконкомпоненты, такие как отображениеOtherModal
просто заменитьModal
Вот и все, чтобы достичь цели повторного использования логического кода «переключателя».
Render Props больше похож наИнверсия управления (IoC), он отвечает только за определение интерфейса или данных и передачу их вам через параметры функции, как использовать эти интерфейсы или данные, полностью зависит от вас.
Если вы не знакомы с Inversion of Control, вы можете прочитать то, что автор написал ранее.Философия IoC во внешнем интерфейсе
Render Props VS HOC
Проблемы, решаемые Render Props икомпоненты более высокого порядкаРешаемые задачи аналогичны, как дляРешить проблему повторного использования кода, то в чем между ними разница, кратко разберем их соответствующие характеристики:
HOC
недостаток:
- Поскольку компоненты более высокого порядка могут быть вложены несколько раз, и нам трудно гарантировать, что имена свойств в каждом компоненте более высокого порядка будут разными, поэтомуСвойства легко переопределяются.
- При использовании компонентов более высокого порядка компоненты более высокого порядка эквивалентнычерный ящик, мы должны посмотреть, как его реализовать, чтобы использовать:
преимущество:
- можно использовать
compose
метод для объединения нескольких компонентов более высокого порядка, а затем использовать
// 不要这么使用
const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent));
// 可以使用一个 compose 函数组合这些高阶组件
// lodash, redux, ramda 等第三方库都提供了类似 `compose` 功能的函数
const enhance = compose(withRouter, connect(commentSelector));
const EnhancedComponent = enhance(WrappedComponent);
- Удобство вызова (ES6 + синтаксис декоратора)
@withData
class App extends React.Component {}
Render Props
- недостаток
- Слишком глубокая вложенность также можетад обратный вызов
- преимущество
- Устраняет недостатки HOC
Render Props и HOC не являются отношениями «или-или».Поняв их соответствующие преимущества и недостатки, мы можем реализовать их подходящим образом в подходящих сценариях.
Что такое React-хуки
React Hooks — это новая функция, представленная в React 16.8, которая позволяет функциональным компонентам иметь тот же (похожий) «жизненный цикл», что и компоненты в стиле классов, чтобы лучше использовать функции React в функциональных компонентах.
Цель введения командой React хуков и вышеупомянутогокомпоненты более высокого порядка,Render PropsТо же самое верно и для повторного использования кода.
Прежде чем разбираться в React Hooks, давайте рассмотрим предыдущие Render Props.Switch
Пример для сравнения:
class Switch extends React.Component {
constructor(props) {
super(props);
this.state = {
on: props.initialState || false,
};
}
toggle() {
this.setState({
on: !this.state.on,
});
}
render() {
return (
<div>{this.props.children({
on,
toggle: this.toggle,
})}</div>
);
}
}
// how to use
const App = () => (
<Switch initialState={false}>{({on, toggle}) => {
<Button onClick={toggle}>Show Modal</Button>
<Modal visible={on} onSure={toggle}></Modal>
}}</Switch>
);
// use hooks
const App = () => {
const [on, setOn] = useState(false);
return (
<div>
<Button onClick={() => setOn(true)}>Show Modal</Button>
<Modal visible={on} onSure={() => setOn(false)}></Modal>
</div>
);
}
Для сравнения мы можем легко обнаружить, что версия Hooks использует только несколько простых API (useState
, setOn
, on
) готовоSwitch
Компонент класса требует 20 строк кода, что значительно сокращает объем кода.
Сокращение кода — лишь одно из преимуществ хуков, но их более важная цель —Мультиплексирование логики состояний. (То же самое верно для высокоуровневых компонентов и Render Props. Повторное использование логики состояния на самом деле является более конкретным утверждением повторного использования кода)
Несколько преимуществ хуков:
- Хотя назначение хуков такое же, как у компонентов более высокого порядка и реквизитов рендеринга, оно предназначено для повторного использования кода, но компоненты хуков более высокого порядка и реквизиты рендеринга проще и понятнее и не вызовут ад вложенности.
- Более простое разделение пользовательского интерфейса и состояния
- Хук может ссылаться на другие хуки
- Решите болевые точки компонентов класса
-
this
подвержен ошибкам - Логика, разделенная на разные циклы объявлений, затрудняет понимание и поддержку кода.
- Высокая стоимость повторного использования кода (компоненты более высокого порядка могут легко увеличить объем кода)
-
Применение хуков React
React официально предоставляет следующие часто используемые хуки:
- Основные крючки:
useState
,useEffect
,useContext
- Дополнительные крючки:
useReducer
,useCallback
,useMemo
,useRef
,useImperativeHandle
,useLayoutEffect
,useDebugValue
useEffect
useEffect
Крючки делают именно то, что следует из названия - для обработки, например.подписка,сбор информации,Манипуляции с DOMДождитесь побочных эффектов. его функция иcomponentDidMount
, componentDidUpdate
а такжеcomponentWillUnmount
Эти функции жизненного цикла аналогичны.
Например, мы хотим отслеживать поле вводаinput
ввод, сuseEffect
Мы можем сделать это так:
function Input() {
const [text, setText] = useState('')
function onChange(event) {
setText(event.target.value)
}
useEffect(() => {
// 类似于 componentDidMount 和 componentDidUpdate 两个生命周期函数
const input = document.querySelector('input')
input.addEventListener('change', onChange);
return () => {
// 类似于 componentWillUnmount
input.removeEventListener('change', onChange);
}
})
return (
<div>
<input onInput={onChange} />
<p>{text}</p>
</div>
)
}
useEffect
Использование хука заключается в передаче функции в качестве первого параметраuseEffect
, в переданной функции мы можем сделать некоторыеиметь побочные эффектытакие вещи, как манипулирование DOM и так далее.
Если входящийuseEffect
функция метода возвращает функцию, котораяфункция, которая возвращаетОн будет вызван, когда компонент будет удален. Здесь мы можем выполнить некоторые операции по очистке, такие как очистка timerID или отмена ранее опубликованной подписки. Может быть более интуитивно понятно написать следующее:
useEffect(function didUpdate() {
// do something effects
return function unmount() {
// cleaning up effects
}
})
когдаuseEffect
Когда передается только один параметр,каждый разrender
будет выполнен позжеuseEffect
функция:
useEffect(() => {
// render 一次,执行一次
console.log('useEffect');
})
когдаuseEffect
Когда второй переданный параметр является массивом,Входящая функция обратного вызова будет выполняться только при изменении значения массива (зависимости), например:
Хотя алгоритм сравнения React обновляет измененные части только при рендеринге DOM, он не распознает
useEffect
внутренние изменения, поэтому разработчик должен сообщить React, какие внешние переменные использовать, через второй параметр.
useEffect(() => {
document.title = title
}, [title])
потому чтоuseEffect
Обратный вызов внутренне использует внешнийtitle
переменная, поэтому при необходимости, только еслиtitle
Если обратный вызов выполняется только при изменении значения, вам нужно только передать массив во втором параметре и записать в массив внутренние зависимые переменные.title
Если значение изменится,useEffect
Внутри обратного вызова вы можете определить, должен ли обратный вызов выполняться через входящие зависимости.
так что если даноuseEffect
Если второй параметр передается в пустом массиве,useEffect
Функция обратного вызова будет выполнена только один раз после первого рендеринга:
useEffect(() => {
// 只会在首次 render 之后执行一次
console.log('useEffect')
}, [])
useContext
В Реакте естьcontext
концепция, которая позволяет намДелитесь состоянием между компонентами без передачиprops
слой за слоем, простой пример:
Редукс использует React
context
Функции позволяют обмениваться данными между компонентами.
const ThemeContext = React.createContext();
function App() {
const theme = {
mode: 'dark',
backgroundColor: '#333',
}
return (
<ThemeContext.Provider value={theme}>
<Display />
</ThemeContext.Provider>
)
}
function Display() {
return (
<ThemeContext.Consumer>
{({backgroundColor}) => <div style={{backgroundColor}}>Hello Hooks.</div>}
</ThemeContext.Consumer>
)
}
НижеuseContext
Версия:
function Display() {
const { backgroundColor } = useContext(ThemeContext);
return (<div style={{backgroundColor}}>Hello Hooks.</div>)
}
Вложенная версияConsumer
:
function Header() {
return (
<CurrentUser.Consumer>
{user =>
<Notifications.Consumer>
{notifications =>
<header>
Hello {user.name}!
You have {notifications.length} notifications.
</header>
}
</Notifications.Consumer>
}
</CurrentUser.Consumer>
);
}
использоватьuseContext
Плоский:
function Header() {
const user = useContext(CurrentUser)
const notifications = useContext(Notifications)
return (
<header>
Hello {user.name}!
You have {notifications.length} notifications.
</header>
)
}
эмм... Этот эффект чем-то похож на использованиеasync
а такжеawait
Чувство обратного вызова доработанного ада.
Это основные хуки React. Для других официально предоставляемых хуков, если вы заинтересованы, рекомендуется прочитать документацию напрямую, в этой статье мы не будем знакомить их по одному.
На что следует обратить внимание при использовании
Хотя React Hooks приносят нам удобство, мы также должны следовать некоторым их соглашениям, иначе эффект станет только контрпродуктивным:
- избегать вЦиклы, условные суждения, вложенные функциивызыватьHooks, чтобы обеспечить стабильность вызывающей последовательности;
- Толькокомпонент определения функцииа такжеHooksможно назватьHooks, избегатькомпонент классаилиОбычная функциявызывать;
- не может быть в
useEffect
используется вuseState
, React сообщит об ошибке; - Компоненты класса не будут заменяться или отбрасываться, нет необходимости принудительно преобразовывать компоненты класса, и эти два метода могут сосуществовать.
Суммировать
Основная идея Render Props состоит в том, чтобы вызыватьthis.props.children()
(Разумеется, это может быть любое другое значение, являющееся именем атрибута функции) Передайте некоторые состояния внутри компонента (обратитесь к callback-функции), а затем получите внутреннее состояние компонента через параметры функции в функции соответствующий атрибут вне компонента, а затем использовать функцию в функции Обработать соответствующий пользовательский интерфейс или логику в . Хуки немного похожи на Render Props.литография(обратитесь к предыдущемуuseContext
)Каштан.
На данный момент было представлено три способа повторного использования кода React:
- Render Props
- Hooks
- HOC
При сравнении обнаруживается, чтоHooksПреимущество метода самое большое, и он решает некоторые болевые точки двух других методов, поэтому рекомендуется использовать его.
Связанное чтение:
- Компоненты высшего порядка в React и сценарии их применения
- Философия IoC во внешнем интерфейсе
- (Часть 2) Коды интервью для производителей интерфейсов среднего и продвинутого уровня