Поиск…


замечания

React.createClass устарел в версии 15.5 и, как ожидается, будет удален в v16 . Для тех, которые все еще требуют этого, есть пакет замены замены . Примеры, использующие его, должны быть обновлены.

Основной компонент

Учитывая следующий HTML-файл:

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>React Tutorial</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
    <script type="text/babel" src="scripts/example.js"></script>
  </body>
</html>

Вы можете создать базовый компонент, используя следующий код в отдельном файле:

скрипты / example.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class FirstComponent extends Component {
  render() {
    return (
      <div className="firstComponent">
        Hello, world! I am a FirstComponent.
      </div>
    );
  }
}
ReactDOM.render(
  <FirstComponent />, // Note that this is the same as the variable you stored above
  document.getElementById('content')
);

Вы получите следующий результат (обратите внимание, что внутри div#content ):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>React Tutorial</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
  </head>
  <body>
    <div id="content">
        <div className="firstComponent">
            Hello, world! I am a FirstComponent.
        </div>
    </div>
    <script type="text/babel" src="scripts/example.js"></script>
  </body>
</html>

Компоненты вложенности

Большая часть возможностей ReactJS - это способность разрешать вложенность компонентов. Возьмите следующие два компонента:

var React = require('react');
var createReactClass = require('create-react-class');

var CommentList = reactCreateClass({
  render: function() {
    return (
      <div className="commentList">
        Hello, world! I am a CommentList.
      </div>
    );
  }
});

var CommentForm = reactCreateClass({
  render: function() {
    return (
      <div className="commentForm">
        Hello, world! I am a CommentForm.
      </div>
    );
  }
});

Вы можете вставлять и ссылаться на эти компоненты в определении другого компонента:

var React = require('react');
var createReactClass = require('create-react-class');

var CommentBox = reactCreateClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList /> // Which was defined above and can be reused
        <CommentForm /> // Same here
      </div>
    );
  }
});

Дальнейшая вложенность может быть выполнена тремя способами: у всех есть свои места для использования.

1. Вложение без использования детей

(продолжение сверху)

var CommentList = reactCreateClass({
  render: function() {
    return (
      <div className="commentList">
        <ListTitle/>
        Hello, world! I am a CommentList.
      </div>
    );
  }
});

Это стиль, в котором A составляет B и B составляет C.

Pros

  • Легко и быстро отделяет элементы пользовательского интерфейса
  • Легко вводить реквизиты до детей на основе состояния родительского компонента

Cons

  • Меньшая видимость архитектуры композиции
  • Меньшее повторное использование

Хорошо, если

  • B и C представляют собой только презентационные компоненты
  • B должен отвечать за жизненный цикл C

2. Вложение с использованием детей

(продолжение сверху)

var CommentBox = reactCreateClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList>
            <ListTitle/> // child
        </CommentList>
        <CommentForm />
      </div>
    );
  }
});

Это стиль, в котором A составляет B и A указывает B на создание C. Больше мощности для родительских компонентов.

Pros

  • Лучшее управление жизненным циклом компонентов
  • Лучшая видимость архитектуры композиции
  • Лучшая повторная эксплуатация

Cons

  • Инъекции реквизита могут стать немного дороже
  • Меньшая гибкость и мощность в детских компонентах

Хорошо, если

  • B должен согласиться составить что-то другое, чем C в будущем или где-то еще
  • A должен контролировать жизненный цикл C

B будет отображать C, используя this.props.children , и нет this.props.children структурированного способа, чтобы B знал, для чего предназначены эти дети. Таким образом, B может обогатить дочерние компоненты, предоставив дополнительные реквизиты, но если B должен точно знать, что они собой представляют, вариант № 3 может быть лучшим вариантом.

3. Вложение с помощью реквизита

(продолжение сверху)

var CommentBox = reactCreateClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList title={ListTitle}/> //prop
        <CommentForm />
      </div>
    );
  }
});

Это стиль, в котором A составы B и B предоставляют возможность для A передать что-то, чтобы составить для определенной цели. Более структурированная композиция.

Pros

  • Композиция как функция
  • Простая проверка
  • Лучшая композиционная способность

Cons

  • Инъекции реквизита могут стать немного дороже
  • Меньшая гибкость и мощность в детских компонентах

Хорошо, если

  • B имеет специфические особенности, определенные для составления чего-либо
  • B должен знать только, как визуализировать не то, что визуализировать

# 3 обычно является обязательным для создания публичной библиотеки компонентов, но также хорошей практикой в ​​целом для создания составных компонентов и четкого определения функций композиции. # 1 - самый простой и быстрый способ сделать что-то, что работает, но # 2 и # 3 должны предоставлять определенные преимущества в различных случаях использования.

Создание компонентов

Это расширение базового примера:

Базовая структура

import React, { Component } from 'react';
import { render } from 'react-dom';

class FirstComponent extends Component {
    render() {
        return (
            <div>
                Hello, {this.props.name}! I am a FirstComponent.
            </div>
        );
    }
}

render(
    <FirstComponent name={ 'User' } />,
    document.getElementById('content')
);

Вышеприведенный пример называется безстоящим компонентом, так как он не содержит состояния (в смысле ответа).

В этом случае некоторые люди считают предпочтительным использовать функциональные компоненты Stateless, которые основаны на функциях стрелок ES6 .

Функциональные компоненты без учета состояния

Во многих приложениях есть интеллектуальные компоненты, которые содержат состояние, но отображают немые компоненты, которые просто получают реквизит и возвращают HTML как JSX. Функциональные компоненты без учета состояния намного более многоразовые и оказывают положительное влияние на ваше приложение.

Они имеют две основные характеристики:

  1. При визуализации они получают объект со всеми реквизитами, которые были переданы
  2. Они должны вернуть JSX для отображения
// When using JSX inside a module you must import React
import React from 'react';
import PropTypes from 'prop-types';

const FirstComponent = props => (
    <div>
        Hello, {props.name}! I am a FirstComponent.
    </div>
);

//arrow components also may have props validation
FirstComponent.propTypes = {
    name: PropTypes.string.isRequired,
}

// To use FirstComponent in another file it must be exposed through an export call:
export default FirstComponent;

Компоненты состояния

В отличие от вышеперечисленных компонентов «без состояния» компоненты «stateful» имеют объект состояния, который может быть обновлен с setState метода setState . Состояние должно быть инициализировано в constructor до его установки:

import React, { Component } from 'react';

class SecondComponent extends Component {
    constructor(props) {
        super(props);

        this.state = {
            toggle: true
        };

        // This is to bind context when passing onClick as a callback
        this.onClick = this.onClick.bind(this);
    }

    onClick() {
        this.setState((prevState, props) => ({
            toggle: !prevState.toggle
        }));
    }
    
    render() {
        return (
            <div onClick={this.onClick}>
                Hello, {this.props.name}! I am a SecondComponent.
                <br />
                Toggle is: {this.state.toggle}
            </div>
        );
    }
}

Расширение компонента с помощью PureComponent вместо Component будет автоматически реализовывать метод жизненного цикла shouldComponentUpdate() с неглубокой поддержкой и сравнением состояний. Это позволяет повысить эффективность вашего приложения за счет уменьшения количества ненужных визуализаций, которые происходят. Это предполагает, что ваши компоненты являются «чистыми» и всегда отображают один и тот же результат с одним и тем же состоянием и реквизитом.

Компоненты более высокого порядка

Компоненты более высокого порядка (HOC) позволяют совместно использовать функциональность компонента.

import React, { Component } from 'react';

const PrintHello = ComposedComponent => class extends Component {
    onClick() {
        console.log('hello');
    }
    
    /* The higher order component takes another component as a parameter 
    and then renders it with additional props */
    render() {
        return <ComposedComponent {...this.props } onClick={this.onClick} />
    }
}

const FirstComponent = props => (
    <div onClick={ props.onClick }>
        Hello, {props.name}! I am a FirstComponent.
    </div>
);

const ExtendedComponent = PrintHello(FirstComponent);

Компоненты более высокого порядка используются, когда вы хотите делиться логикой между несколькими компонентами независимо от того, насколько они отличаются.

Недостатки setState

Следует соблюдать осторожность при использовании setState в асинхронном контексте. Например, вы можете попытаться вызвать setState в setState вызове setState get:

class MyClass extends React.Component {
    constructor() {
        super();

        this.state = {
            user: {}
        };
    }

    componentDidMount() {
        this.fetchUser();
    }

    fetchUser() {
        $.get('/api/users/self')
            .then((user) => {
                this.setState({user: user});
            });
    }

    render() {
        return <h1>{this.state.user}</h1>;
    }
}

Это может вызвать проблемы - если вызываемый вызов вызывается после демонтажа Component , то this.setState не будет функцией. Всякий раз, когда это так, вы должны быть осторожны, чтобы ваше использование setState .

В этом примере вы можете отменить запрос XHR, когда компонент отключится:

class MyClass extends React.Component {
    constructor() {
        super();

        this.state = {
            user: {},
            xhr: null
        };
    }

    componentWillUnmount() {
        let xhr = this.state.xhr;

        // Cancel the xhr request, so the callback is never called
        if (xhr && xhr.readyState != 4) {
            xhr.abort();
        }
    }

    componentDidMount() {
        this.fetchUser();
    }

    fetchUser() {
        let xhr = $.get('/api/users/self')
            .then((user) => {
                this.setState({user: user});
            });

        this.setState({xhr: xhr});
    }
}

Асинхронный метод сохраняется как состояние. В componentWillUnmount вы выполняете всю свою очистку - включая отмену запроса XHR.

Вы могли бы также сделать что-то более сложное. В этом примере я создаю функцию stateSetter, которая принимает этот объект в качестве аргумента и предотвращает this.setState когда this.setState функция cancel :

function stateSetter(context) {
    var cancelled = false;
    return {
        cancel: function () {
            cancelled = true;
        },
        setState(newState) {
            if (!cancelled) {
                context.setState(newState);
            }
        }
    }
}

class Component extends React.Component {
    constructor(props) {
        super(props);
        this.setter = stateSetter(this);
        this.state = {
            user: 'loading'
        };
    }
    componentWillUnmount() {
        this.setter.cancel();
    }
    componentDidMount() {
        this.fetchUser();
    }
    fetchUser() {
        $.get('/api/users/self')
            .then((user) => {
                this.setter.setState({user: user});
            });
    }
    render() {
        return <h1>{this.state.user}</h1>
    }
}

Это работает, потому что cancelled переменная видна в закрытии setState мы создали.

Реквизит

Реквизиты - это способ передачи информации в компонент React, у них может быть любой тип, включая функции, иногда называемые обратными вызовами.

В реквизитах JSX передаются синтаксис атрибута

<MyComponent userID={123} />

Внутри определения для MyComponent userID теперь будет доступен из объекта реквизита

// The render function inside MyComponent
render() {
    return (
        <span>The user's ID is {this.props.userID}</span>
    )
}

Важно определить все props , их типы и, где применимо, их значение по умолчанию:

// defined at the bottom of MyComponent
MyComponent.propTypes = {
    someObject: React.PropTypes.object,
    userID: React.PropTypes.number.isRequired,
    title: React.PropTypes.string
};

MyComponent.defaultProps = {
    someObject: {},
    title: 'My Default Title'
}

В этом примере prop someObject является необязательным, но требуется userID prop. Если вы не предоставите userID MyComponent , во время выполнения движок React отобразит консоль, предупреждающую вас о том, что требуемая поддержка не была предоставлена. Остерегайтесь, однако, это предупреждение отображается только в версии версии библиотеки React, производственная версия не будет регистрировать никаких предупреждений.

Использование defaultProps позволяет упростить

const { title = 'My Default Title' } = this.props;
console.log(title);

в

console.log(this.props.title);

Это также является гарантией использования array object и functions . Если вы не предоставляете опору по умолчанию для объекта, следующее будет вызывать ошибку, если оповещение не передается:

if (this.props.someObject.someKey)

В приведенном выше примере this.props.someObject не undefined и поэтому проверка someKey вызовет ошибку, и код сломается. С помощью defaultProps вы можете безопасно использовать вышеуказанную проверку.

Состояние компонентов - Динамический пользовательский интерфейс

Предположим, что мы хотим иметь следующее поведение: у нас есть заголовок (например, элемент h3) и при нажатии на него мы хотим, чтобы он стал полем ввода, чтобы мы могли изменить название заголовка. React делает это очень простым и интуитивно понятным с использованием состояний компонентов, а если иными словами. (Пояснение ниже)

// I have used ReactBootstrap elements. But the code works with regular html elements also
var Button = ReactBootstrap.Button;
var Form = ReactBootstrap.Form;
var FormGroup = ReactBootstrap.FormGroup;
var FormControl = ReactBootstrap.FormControl;

var Comment = reactCreateClass({
  getInitialState: function() {
    return {show: false, newTitle: ''};
  },

  handleTitleSubmit: function() {
     //code to handle input box submit - for example, issue an ajax request to change name in database
  },

  handleTitleChange: function(e) {
    //code to change the name in form input box. newTitle is initialized as empty string. We need to update it with the string currently entered by user in the form
    this.setState({newTitle: e.target.value});
  },

  changeComponent: function() {
    // this toggles the show variable which is used  for dynamic UI
    this.setState({show: !this.state.show)};
  },

  render: function() {

    var clickableTitle;

    if(this.state.show) {
        clickableTitle = <Form inline onSubmit={this.handleTitleSubmit}>
                             <FormGroup controlId="formInlineTitle">
                                 <FormControl type="text" onChange={this.handleTitleChange}>
                             </FormGroup>
                         </Form>;
    } else {
        clickabletitle = <div>
                            <Button bsStyle="link" onClick={this.changeComponent}>
                                <h3> Default Text </h3>
                            </Button>
                         </div>;
    }

    return (
        <div className="comment">
            {clickableTitle}
        </div>
    );
  }
});

ReactDOM.render(
    <Comment />, document.getElementById('content')
);

Основной частью кода является переменная clickableTitle . На основании переменного состояния шоу, это может быть либо элемент формы или элемент Button. Реакция позволяет вложенность компонентов.

Таким образом, мы можем добавить элемент {clickableTitle} в функции рендеринга. Он ищет переменную clickableTitle. Основываясь на значении «this.state.show», он отображает соответствующий элемент.

Вариации функциональных компонентов без учета состояния

const languages = [
  'JavaScript',
  'Python',
  'Java',
  'Elm',
  'TypeScript',
  'C#',
  'F#'
]

// one liner
const Language = ({language}) => <li>{language}</li>

Language.propTypes = {
  message: React.PropTypes.string.isRequired
}

/**
* If there are more than one line.
* Please notice that round brackets are optional here,
* However it's better to use them for readability
*/
const LanguagesList = ({languages}) => {
  <ul>
    {languages.map(language => <Language language={language} />)}
  </ul>
}

LanguagesList.PropTypes = {
  languages: React.PropTypes.array.isRequired
}

/**
 * This syntax is used if there are more work beside just JSX presentation
 * For instance some data manipulations needs to be done.
 * Please notice that round brackets after return are required,
 * Otherwise return will return nothing (undefined)
 */
const LanguageSection = ({header, languages}) => {
  // do some work
  const formattedLanguages = languages.map(language => language.toUpperCase())
  return (
    <fieldset>
      <legend>{header}</legend>
      <LanguagesList languages={formattedLanguages} />
    </fieldset>
  )
}

LanguageSection.PropTypes = {
  header: React.PropTypes.string.isRequired,
  languages: React.PropTypes.array.isRequired
}

ReactDOM.render(
  <LanguageSection 
    header="Languages" 
    languages={languages} />,
  document.getElementById('app')
)

Здесь вы можете найти рабочий пример.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow