Эту статью прочитал Микеле Бертоли«Шаблоны проектирования React и лучшие практики»Заметки о чтении книги, пожалуйста, нажмите, чтобы поддержать автораздесьКупить.
Talk is cheap, just show me the code.
Можно много говорить о чепухе, и я перейду непосредственно к галантерее.
Об условном решении в функции рендеринга
В React есть ситуация, когда нам часто нужно решить, следует ли отображать определенные компоненты на основе условных суждений. Так:
<div>
{ isLoggedIn ? <LogoutButton /> : <LoginButton /> }
{ visible && <Modal /> }
</div>
Когда условное суждение становится более сложным, мы можем использовать методы и вычисляемые свойства, чтобы заменить троичную операцию и или или суждение.
handleShowLoginButton() {
return this.isLoggedIn && this.isAuthed;
}
get getVisible() {
return this.visible && this.displayMode === "normal"
}
render() {
return (<div>
{ handleShowLoginButton() ? <LogoutButton /> : <LoginButton /> }
{ getVisible && <Modal /> }
</div>)
}
Затем приходит черная технология, когда мы хотим извлечь эту логику суждения из функции рендеринга рендеринга, чтобы функция рендеринга отвечала только за рендеринг. нам нужно использоватьrender-if
render-only-if
jsx-control-statements
Эти помощники зависят от него.
Пожалуйста, посмотри:
const isShowLoginButton = renderIf(
this.isLoggedIn && this.isAuthed
)
return (<div>
{ isShowLoginButton(<LoginButton />) } {/* 完了结果 LogoutButton 我还需要另外写一个 isShowLogoutButton 的 renderIf 去判断显示与否吗 */}
</div>)
Тогда render-only-if по сути является функцией более высокого порядка, которая более элегантна по форме, чем render-if .
const LoginButtonOnlyIf = onlyIf(
({ isLoggedIn && isAuthed }) => {
return isLoggedIn && isAuthed
}
)(LoginButton)
return (
<LoginButtonOnlyIf
isLoggedIn={isLoggedIn}
isAuthed={isAuthed}
/>
)
Суммировать:
- Если это просто условное суждение, троичная сумма и/или оператор удовлетворили потребности большинства людей;
- Если вы хотите разделить проблемы, renderIf — хорошая идея;
- Наконец, если вы хотите, чтобы ваша логика оценки использовалась повторно (как если несколько страниц и несколько компонентов должны использовать логику для оценки состояния входа и разрешений пользователя), вы можете использовать onlyIf для создания повторно используемых компонентов более высокого порядка в проекте.
Затем мы, наконец, рассмотрим использование этой отвратительной штуки jsx-control-statements:
<If condition={this.handleShowLoginButton}>
<LoginButton />
</If>
<When condition={this.handleShowLoginButton}>
<LoginButton />
</When>
<When condition={!this.handleShowLoginButton}>
<LogoutButton /> // => 好了这下终于看见我们的 LogoutButton 出现了
</When>
<Otherwise>
<p>oops.. no condition matched.</p>
</Otherwise>
<ul>
<For each="resultItem" of={this.resultList}>
<li>{resultItem.name}</li>
</For>
// => {resultList.map(resultItem => <li>{resultItem.name}</li>)}
</ul>
Разработка повторно используемых компонентов
Это касается производительности и ремонтопригодности.
Всегда имейте в виду, что состояние установки запускает повторную визуализацию компонента. Поэтому в состоянии должны храниться только те значения, которые используются методом рендеринга.
Ниже приведены Дэн Абрамов (я не знаю, кто он), чтобы создать шаг, чтобы помочь нам сделать правильный выбор статуса:
function shouldIKeepSomethingInReactState() {
if (canICalculateItFromProps()) {
// 不要把 props 属性直接在 state 状态中使用,
// 应该直接在 render() 函数里计算使用它们
return false
}
if (!amIUsingItInRenderMethod()) {
// 不要把没有参与渲染的数据放进 state 状态里,
// 换句话说就是只有需要涉及到组件 render 渲染更新的数据才放到 state 里
return false
}
// 除了以上情况,都可以使用状态。
return true;
}
Что касается проверки типа реквизита, React предоставляетpropTypes
свойства, которые мы можем использовать:
const Button = ({text}) => <button>{text}</button>
Button.propTypes = {
text: React.PropTypes.string
}
Но на самом деле в мире TypeScript мы можем напрямую объявить интерфейс свойства prop для нашего компонента React в виде класса шаблона:
interface IButtonProps = {
text: string;
}
class ButtonClass extend React.Component<IButtonProps, IButtonStates> {}
// => 顺带连 state 属性检验也可以加进来
Далее автоматически сгенерируйте документацию для компонента, используяreact-docgen
этот инструмент.
import React from 'react';
/**
* Sheet 组件
*/
const Sheet = ({title}) => <div>{title}</div>
Sheet.prototype = {
/**
* Sheet 标题
*/
title: React.PropTypes.string
}
бегатьreact-docgen Sheet.js
Результатом является следующее описание json:
{
"description": "Sheet 组件",
"displayName": "Sheet",
"methods": [],
"props": {
"title": {
"type": {
"name": "string"
},
"required": false,
"description": "Sheet 标题"
}
}
}
Используя этот json-файл в качестве входных данных для проекта документации переднего плана команды, вы можете автоматически генерировать полезные инструкции по документации компонентов.
Хорошо, давайте выявим самого большого убийцу в отрасли.storybook.
npm i --save @kadira/react-storybook-addon
(Кажется, что @kadira/react-storybook-addon был утилизирован. Согласно документу рекомендуется написать собственный сборник рассказов на официальном сайте)
Документы рассказа помещаются вstories
, мы создаем лист.js в папке историй, чтобы определить документ истории компонентов, которые мы определили выше.
// => stories/sheet.js
import React from 'react';
import Sheet from '../src/components/Sheet';
import { storiesOf } from '@kadira/storybook'
storiesOf('Sheet', module)
.add('没有 title 属性的 Sheet 的故事..', () => (
<Sheet/>
))
Но мы должны написать историю, а также сначала настроить Storybook в корневом каталоге:
// => .storybook/config.js => 根目录下创建 .storybook 文件夹
import { configure } from '@kadira/storybook';
function loadStories() {
require('../src/stories/sheet')
}
configure(loadStories, module)
Наконец, мы добавляем скрипт в наш package.json для запуска нашей истории.
"storybook": "start-storybook -p 9001"
После запуска вы можете увидеть файл истории на порту 9001.
Рассмотрим детальнее компонент
Что касается компонентов-контейнеров и компонентов-дураков, я не буду о них здесь говорить. Ведь это первичный контент, поэтому считаться с ним непросто. Давайте сразу к делу.
Например, в самом простом случае реализуйте компонент более высокого порядка, который добавляет к компоненту имя класса:
const withClassName = Component => props => (
<Component {...props} className="my-class" />
)
Вышеупомянутое свойство — это просто перенесенная опора.На самом деле мы можем переместить все остальные свойства компонента, кроме опоры.
const withTimer = Component => (
class extends React.Component {
constructor(props) {
super(props)
this.state = {
timer: null
}
}
componentDidMount() {
this.timer = setTimeInterval(() => {
console.log('每个1.5s打印一次日志哈哈哈')
}, 1500)
}
componentWillUnmount() {
clearInterval(this.timer)
}
render() {
// => 原封不动把接收到的 props 传给 Component
// state 传入 Compnent 其实是可选项,根据实际需求决定
return <Component {...this.props} {...this.state} />
}
}
)
// => 然后我们就可以给普通的组件加上定时打印日志的功能啦
const SheetWithTimer = withTimer(Sheet);
потомrecomposeЭта библиотека уже предоставила нам некоторые высокоуровневые компоненты для практических сценариев.
Далее, давайте сделаем несколько трюков, см.функциональный подкомпонентКак играть. Прежде всего, наш withClassName выше явно слишком мал, и className записано до смерти! ? Не пишите все до смерти, вам нужно изменить это через несколько минут.
Очевидно, нам нужно сделать еще один уровень логики в компоненте withClassName, а затем динамически передать className дочернему компоненту после решения. На этот раз, чтобы сделать трюк, мы решили использовать режим подкомпонента функции.
const withClassName = ({children}) => children('my-class'); // => wft,这里 'my-class' 还不照样是写死的...
<withClassName>
{(classname) => <Component className={classname} />}
</withClassName>
Затем мы видим бесконечные возможности... Хотя withClassName по-прежнему является компонентом без состояния, мы можем добавить к нему жизненные крючки, методы функций и состояние, как и компонент withTimer. Тогда в рендере разница (мы не используем
render() {
return <Component {...props} /> // => 一般做法
renturn {children(props)} // => 函数子组件做法
}
Возможно, более подходящим примером является сценарий, в котором компоненту более высокого порядка необходимо сделать HTTP-запрос, а данные, возвращенные из запроса, передаются дочернему компоненту для рендеринга.
<Fetch url="...">
{data => <MyComp data={data} />}
</Fetch>
Наконец, давайте поговорим о CSS
Прежде всего, просто и грубо, мы можем написать стиль прямо в элементе html, который выглядит в мире jsx так:
<div style={{ fonSize: this.state.fontSize }} />
Тогда встроенный стиль стиля имеет недостаток, заключающийся в том, что вы не можете написать его напрямую.媒体查询
а также伪类伪元素
,Конечно动画
(Небольшие межстраничные объявления: используйте библиотеку реагирования на анимацию.react-motionВы не можете написать это.
такRadiumвозник.
С Radium вы можете сделать это произвольно:
import radium from 'radium'
const myStyle = {
fontSize: '12px',
':hover': {
color: '#abc'
},
'@media (min-width: 720px)': {
color: '#121212'
}
}
const Div = () => <div style={myStyle} />
export default radium(Div);
Конечно, чтобы использовать медиа-запросы, вам также необходимо сохранить элемент styleroot на самом внешнем уровне, иначе Radium не будет знать, где находится корневой элемент вашего медиа-запроса.
import { StyleRoot } from 'radium'
class App extends Component {
render() {
return (
<StyleRoot>
<router-view />
</StyleRoot>
)
}
}
Конечно, мы можем совершенно не использовать встроенные стили, а использовать css-модули на основе className.
import styles from './index.less'
render() {
return {
<div className={styles.myDiv} />
}
}
/* index.less */
.myDiv {
font-size: 12px
}
/* 默认模块会生成一堆我们看不懂的英文类名,如果想要让类名不作用在局部而是全局,可以使用 :global */
:global .myGlobalDiv {
font-size: 15px
}
/* 我们还可以使用 composes 把其他类的样式混进来 */
.myDivCop {
composes: .myDiv;
color: '#101010'
}
Кроме того, если ваше className не хочет писать форму style.[имя класса], но хочет написать форму строкового имени класса напрямую, вы можете позаимствоватьreact-css-modulesэта библиотека.
Затем используется эта поза:
import cssModules from 'react-css-modules'
import styles from './index.less'
class DivComp extends Component {
render() {
return (
<div className='myDiv' />
)
}
}
export cssModules(DivComp, styles)
Затем есть еще один вызов, который может стать тенденцией в будущем.styled-componentsПарень, потому что арендодатель действительно не может учиться, поэтому я не буду говорить об этом здесь.
Отсканируйте код, чтобы получить вознаграждение, и сообщите мне тему контента, который вы хотите прочитать в следующем выпуске (милое лицо)
Шучу, добро пожаловать, чтобы оставить сообщение в области комментариев и сообщить мне тему контента, которую вы хотите прочитать,Я выберу только то, что хочу, и я тоже не выберу хахахаЯ постараюсь найти время для демонстраций и расширенного чтения. Вы также можете обратить внимание и призвать меня обновить статью в следующем выпуске.