Как лучше всего оптимизировать этот компонент React, чтобы сделать его многоразовым? - PullRequest
0 голосов
/ 02 марта 2020

Я новичок в React, и на данный момент я видел только основы. У меня на работе есть задача, состоящая в том, чтобы сделать компонент Условий обслуживания, который находится в OptInPage.jsx, повторно используемым. Я попробовал несколько вещей, не понимая, что я делаю, я посмотрел документацию, но примеры кажутся более основательными, чем моя проблема. Я думал об извлечении страницы и создании нового компонента TermsOfServices для интеграции между тегами jsx. Проблема здесь в том, что, с одной стороны, должно быть свойство «только для чтения», чтобы пользователь не мог изменять содержимое, а с другой стороны, я не совсем понимаю, как передавать реквизиты из моего компонента OptinPage в мой новый компонент TermsOfServices. Извините заранее, если моя проблема может показаться глупой или что-то в этом роде, я действительно застрял с этим. Заранее благодарю за помощь.

OptinPage.jsx

import API from 'api';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, intlShape } from 'react-intl';
import {
  Block,
  BlockTitle,
  Col,
  Fab,
  Icon,
  Link,
  NavRight,
  Navbar,
  Page,
  Popup,
  Preloader,
} from 'framework7-react';
import { connect } from 'react-refetch';
import ReactHtmlParser from 'react-html-parser';
import './OptInPage.scss';

class OptInPage extends PureComponent {
  static propTypes = {
    agreeTosFunc: PropTypes.func.isRequired,
    agreeTos: PropTypes.object,
    logout: PropTypes.func.isRequired,
    onSucceeded: PropTypes.func,
    opened: PropTypes.bool.isRequired,
    tos: PropTypes.object.isRequired,
  };

  static contextTypes = {
    apiURL: PropTypes.string,
    intl: intlShape,
    loginToken: PropTypes.string,
    logout: PropTypes.func,
    userId: PropTypes.string,
  };

  static defaultProps = {
    agreeTos: {},
    onSucceeded: () => {},
  };

  state = {
    currentTos: -1,
  };

  componentDidUpdate(prevProps) {
    const {
      agreeTos,
      onSucceeded,
      opened,
      tos,
    } = this.props;
    const { currentTos } = this.state;

    /* Reset currentTos on opened */
    if (!prevProps.opened && opened) {
      this.setState({ currentTos: -1 });
    }

    /* Prepare for first tos after receiving all of them */
    if (
      prevProps.tos.pending &&
      tos.fulfilled &&
      tos.value.length &&
      currentTos < 0
    ) {
      this.setState({ currentTos: 0 });
    }

    /* When sending ToS agreement is done */
    if (
      prevProps.agreeTos.pending &&
      agreeTos.fulfilled
    ) {
      onSucceeded();
    }
  }

  handleNext = () => {
    const { agreeTosFunc, tos } = this.props;
    const { currentTos: currentTosId } = this.state;
    const termsOfServices = tos.value;
    const done = currentTosId + 1 === termsOfServices.length;
    this.setState({ currentTos: currentTosId + 1 });
    if (done) {
      agreeTosFunc(termsOfServices.map((v) => v._id));
    }
  };

  render() {
    const { logout, opened, tos } = this.props;
    const { intl } = this.context;
    const { formatMessage } = intl;
    const { currentTos: currentTosId } = this.state;
    const termsOfServices = tos.value;
    const currentTermsOfServices = termsOfServices && termsOfServices[currentTosId];
    const loaded = termsOfServices && !tos.pending && tos.fulfilled;
    const htmlTransformCallback = (node) => {
      if (node.type === 'tag' && node.name === 'a') {
        // eslint-disable-next-line no-param-reassign
        node.attribs.class = 'external';
      }
      return undefined;
    };

    return (
      <Popup opened={opened} className="demo-popup-swipe" tabletFullscreen>
        <Page id="optin_page">
          <Navbar title={formatMessage({ id: 'press_yui_tos_title' })}>
            <NavRight>
              <Link onClick={() => logout()}>
                <FormattedMessage id="press_yui_comments_popup_edit_close" />
              </Link>
            </NavRight>
          </Navbar>
          { (!loaded || !currentTermsOfServices) && (
            <div id="optin_page_content" className="text-align-center">
              <Block className="row align-items-stretch text-align-center">
                <Col><Preloader size={50} /></Col>
              </Block>
            </div>
          )}
          { loaded && currentTermsOfServices && (
            <div id="optin_page_content" className="text-align-center">
              <h1>
                <FormattedMessage id="press_yui_tos_subtitle" values={{ from: currentTosId + 1, to: termsOfServices.length }} />
              </h1>
              <BlockTitle>
                {ReactHtmlParser(
                  currentTermsOfServices.title,
                  { transform: htmlTransformCallback },
                )}
              </BlockTitle>
              <Block strong inset>
                <div className="tos_content">
                  {ReactHtmlParser(
                    currentTermsOfServices.html,
                    { transform: htmlTransformCallback },
                  )}
                </div>
              </Block>
              <Fab position="right-bottom" slot="fixed" color="pink" onClick={() => this.handleNext()}>
                {currentTosId + 1 === termsOfServices.length &&
                  <Icon ios="f7:check" aurora="f7:check" md="material:check" />}
                {currentTosId !== termsOfServices.length &&
                  <Icon ios="f7:chevron_right" aurora="f7:chevron_right" md="material:chevron_right" />}
              </Fab>
              {currentTosId > 0 && (
              <Fab position="left-bottom" slot="fixed" color="pink" onClick={() => this.setState({ currentTos: currentTosId - 1 })}>
                <Icon ios="f7:chevron_left" aurora="f7:chevron_left" md="material:chevron_left" />
              </Fab>
              )}
            </div>
          )}
        </Page>
      </Popup>
    );
  }
}

export default connect.defaults(new API())((props, context) => {
  const { apiURL, userId } = context;
  return {
    tos: {
      url: new URL(`${apiURL}/tos?outdated=false&required=true`),
    },
    agreeTosFunc: (tos) => ({
      agreeTos: {
        body: JSON.stringify({ optIn: tos }),
        context,
        force: true,
        method: 'PUT',
        url: new URL(`${apiURL}/users/${userId}/optin`),
      },
    }),
  };
})(OptInPage);

1 Ответ

0 голосов
/ 02 марта 2020

Если я вас правильно понял, вам нужно что-то подобное. Прежде всего, вам нужно создать новый файл с вашим компонентом и поместить туда следующий код

// don't forget to clean the unused imports
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, intlShape } from 'react-intl';
import {Block, BlockTitle, Col, Fab, Icon, Link, NavRight, Navbar, Page, Popup, Preloader } from 'framework7-react';
import ReactHtmlParser from 'react-html-parser';
export default class YourCLassName extends PureComponent<Props, State> {
    static propTypes = {
        currentTosId: PropTypes.object, // write correct type here
        termsOfServices: PropTypes.object, // write correct type here
        currentTermsOfServices: PropTypes.string, // write correct type here
        handleNext: PropTypes.func, // write correct type here
        handlePrev: PropTypes.func, // write correct type here
    };

    function htmlTransformCallback (node) => {
      if (node.type === 'tag' && node.name === 'a') {
        // eslint-disable-next-line no-param-reassign
        node.attribs.class = 'external';
      }
      return undefined;
    };


  render() {
    const { currentTosId, termsOfServices, currentTermsOfServices } = this.props;

    return (
        <div id="optin_page_content" className="text-align-center">
          <h1>
            <FormattedMessage id="press_yui_tos_subtitle" values={{ from: currentTosId + 1, to: termsOfServices.length }} />
          </h1>
          <BlockTitle>
            {ReactHtmlParser(
              currentTermsOfServices.title,
              { transform: htmlTransformCallback },
            )}
          </BlockTitle>
          <Block strong inset>
            <div className="tos_content">
              {ReactHtmlParser(
                currentTermsOfServices.html,
                { transform: htmlTransformCallback },
              )}
            </div>
          </Block>
          <Fab position="right-bottom" slot="fixed" color="pink" onClick={() => this.props.handleNext()}>
            {currentTosId + 1 === termsOfServices.length &&
              <Icon ios="f7:check" aurora="f7:check" md="material:check" />}
            {currentTosId !== termsOfServices.length &&
              <Icon ios="f7:chevron_right" aurora="f7:chevron_right" md="material:chevron_right" />}
          </Fab>
          {currentTosId > 0 && (
          <Fab position="left-bottom" slot="fixed" color="pink" onClick={() => this.props.handlePrev())}>
            <Icon ios="f7:chevron_left" aurora="f7:chevron_left" md="material:chevron_left" />
          </Fab>
          )}
        </div>
    );
}
}

А в OptinPage.jsx вам нужно заменить весь этот код, который касается вашего нового компонента, на

<YourComponentName currentTosId={this.state.currentTosId} termsOfServices={termsOfServices} currentTermsOfServices={currentTermsOfServices} handleNext={this.handleNext} handlePrev={this.handlePrev} />

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...