Полная реализация с нуля!

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



Требования

Для начала мы рассмотрим предварительные условия для нашего виджета:

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

Реализация компонентов

Давайте начнем с реквизита нашего компонента стартового рейтинга. С помощью этого реквизита мы можем настроить наш слайдер. Вот как будет выглядеть ваш файл configure.js.

{
  defaultState: number,
  emptyColor: string,
  fillColor: string,
  height: number,
  labelText: func,
  maxValue: number,
  onChangeHover: func,
  onChangeValue: func,
  readOnly: bool,
  width: number,
}

Нам необходимо реализовать следующие функции в компоненте Star Rating:

  1. Усовершенствуйте функции установки и наведения, чтобы удовлетворить требования клиентов.
  2. Включите все конфигурации виджетов в контекст для удобства доступа.
  3. Отобразите компоненты Label и StarsList.
  4. Компонент будет хранить текущий рейтинг и значения наведения в своем локальном состоянии. Чтобы избежать чрезмерного вложения свойств и компонентов, я рекомендую использовать Context для предоставления конфигурации и текущего состояния виджета.
  5. Функции setRatingFn и setHoverFn расширяют события настраиваемой функциональностью и предоставляют текущие значения. Эти методы отключены, когда компонент находится в режиме только для чтения.

Компонент звездного рейтинга.js

import React, {useState, createContext} from "react";
import PropTypes from "prop-types";

import StarRatingLabel from "./components/StarRatingLabel";
import StarsList from "./components/StarsList";

export const StarRatingContext = createContext();

export default function StarRating({
  defaultState,
  emptyColor,
  fillColor,
  height,
  labelText,
  maxValue,
  onChangeHover,
  onChangeValue,
  readOnly,
  width,
}) {
  const [rating, setRating] = useState(defaultState);
  const [hover, setHover] = useState(null);

  const setRatingFn = (value) => {
    if (readOnly) return;

    setRating(value);
    onChangeValue(value);
  }

  const setHoverFn = (value) => {
    if (readOnly) return;

    setHover(value);
    onChangeHover(value);
  }

  return (
    <>
      <StarRatingContext.Provider
        value={{
          emptyColor,
          fillColor,
          height,
          hover,
          labelText,
          rating,
          setHover: setHoverFn,
          setRating: setRatingFn,
          width,
          maxValue,
        }}
      >
        <>
          <StarRatingLabel />
          <StarsList />
        </>
      </StarRatingContext.Provider>
    </>
  );
}

StarRating.propTypes = {
  defaultState: PropTypes.number,
  emptyColor: PropTypes.string,
  fillColor: PropTypes.string,
  height: PropTypes.number,
  labelText: PropTypes.func,
  maxValue: PropTypes.number,
  onChangeHover: PropTypes.func,
  onChangeValue: PropTypes.func,
  readOnly: PropTypes.bool,
  width: PropTypes.number,
};

StarRating.defaultProps = {
  defaultState: 0,
  emptyColor: "grey",
  fillColor: "#edaa10",
  height: 53,
  labelText: (value) => `Rating is: ${value}`,
  maxValue: 5,
  onChangeHover: () => {},
  onChangeValue: () => {},
  readOnly: false,
  width: 53,
};

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

Компонент звездного рейтинга Component.js

import React, { useContext } from "react";

import { StarRatingContext } from "../StarRating";

function StarRatingLabel() {
  const { rating, labelText } = useContext(StarRatingContext);

  return (
    <div>{labelText(rating)}</div>
  );
}

export default StarRatingLabel;

Компонент «StarsList» отображает количество компонентов «Звезда» на основе предоставленной конфигурации. По умолчанию он показывает пять звезд.

Компонент списка звезд.js

import React, { useContext } from "react";

import Star from "./Star";

import { StarRatingContext } from "../StarRating";

function StarsList() {
  const { maxValue } = useContext(StarRatingContext);

  return (
    <div className="star-rating">
      {[...Array(maxValue)].map((star, index) => {
        const value = index + 1;

        return (
          <Star
            key={index}
            value={value}
          />
        );
      })}
    </div>
  );
}

export default StarsList;

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

Звездный компонент.js

import React, { useContext } from "react";

import { StarRatingContext } from '../StarRating';

function Star({ value }) {
  const {
    emptyColor,
    fillColor,
    height,
    hover,
    rating,
    setHover,
    setRating,
    width,
  } = useContext(StarRatingContext);

  return (
    <div
      className="star"
      onClick={() => setRating(value)}
      onMouseEnter={() => setHover(value)}
      onMouseLeave={() => setHover(null)}
    >
      <svg
        data-rating={value}
        fill={value <= (hover || rating) ? fillColor : emptyColor}
        height={height}
        viewBox="0 0 25 25"
        width={width}
      >
        <polygon
          strokeWidth="0"
          points="9.9, 1.1, 3.3, 21.78, 19.8, 8.58, 0, 8.58, 16.5, 21.78"
        />
      </svg>
    </div>
  );
}

export default Star;

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

Оптимизация:

Наблюдая за компонентом Star, становится очевидным, что мы можем каждый раз исключать создание встроенных функций. Чтобы улучшить код нашего компонента Star, мы должны включить атрибут данных с соответствующим значением для звезды. Кроме того, нам нужно вызывать методы onClick, onMouseEnter и onMouseLeave, не прибегая к встроенным функциям.

Оптимизация звездных компонентов.js

import React, { useContext } from "react";

import { StarRatingContext } from '../StarRating';

function Star({ value }) {
  const {
    emptyColor,
    fillColor,
    height,
    hover,
    rating,
    setHover,
    setRating,
    width,
  } = useContext(StarRatingContext);

  return (
    <div
      className="star"
      data-star={value}
      onClick={setRating}
      onMouseEnter={setHover}
      onMouseLeave={setHover}
    >
      <svg
        fill={value <= (hover || rating) ? fillColor : emptyColor}
        height={height}
        viewBox="0 0 25 25"
        width={width}
      >
        <polygon
          strokeWidth="0"
          points="9.9, 1.1, 3.3, 21.78, 19.8, 8.58, 0, 8.58, 16.5, 21.78"
        />
      </svg>
    </div>
  );
}

export default Star;

Два метода, а именно setRatingFn и setHoverFn, были изменены в компоненте Star Rating. Атрибут данных позволяет нам получить значение, которое затем можно использовать. Чтобы аннулировать состояние наведения при возникновении события OnmouseLeave, мы можем либо ввести необходимый код, либо создать отдельный метод и вызвать его.

Компонент звездного рейтинга Optimization.js

import React, {useState, createContext} from "react";
import PropTypes from "prop-types";

import StarRatingLabel from "./components/StarRatingLabel";
import StarsList from "./components/StarsList";

export const StarRatingContext = createContext();

export default function StarRating({
  defaultState,
  emptyColor,
  fillColor,
  height,
  labelText,
  maxValue,
  onChangeHover,
  onChangeValue,
  readOnly,
  width,
}) {
  const [rating, setRating] = useState(defaultState);
  const [hover, setHover] = useState(null);

  const setRatingFn = (e) => {
    if (readOnly) return;

    const value = e.currentTarget.dataset.star;

    setRating(value);
    onChangeValue(value);
  }

  const setHoverFn = (e) => {
    if (readOnly) return;

    const value = e.type === 'mouseleave' ? null : e.currentTarget.dataset.star;

    setHover(value);
    onChangeHover(value);
  }

  return (
    <>
      <StarRatingContext.Provider
        value={{
          emptyColor,
          fillColor,
          height,
          hover,
          labelText,
          rating,
          setHover: setHoverFn,
          setRating: setRatingFn,
          width,
          maxValue,
        }}
      >
        <>
          <StarRatingLabel />
          <StarsList />
        </>
      </StarRatingContext.Provider>
    </>
  );
}

StarRating.propTypes = {
  defaultState: PropTypes.number,
  emptyColor: PropTypes.string,
  fillColor: PropTypes.string,
  height: PropTypes.number,
  labelText: PropTypes.func,
  maxValue: PropTypes.number,
  onChangeHover: PropTypes.func,
  onChangeValue: PropTypes.func,
  readOnly: PropTypes.bool,
  width: PropTypes.number,
};

StarRating.defaultProps = {
  defaultState: 0,
  emptyColor: "grey",
  fillColor: "#edaa10",
  height: 53,
  labelText: (value) => `Rating is: ${value}`,
  maxValue: 5,
  onChangeHover: () => {},
  onChangeValue: () => {},
  readOnly: false,
  width: 53,
};

Последние мысли

Я надеюсь, что вы нашли эту статью информативной и полезной в вашем путешествии по javascript к пониманию и реализации пользовательских виджетов звездного рейтинга с нуля. Поделитесь этой замечательной статьей со своими друзьями по Javascript. Удачного кодирования!

До встречи в моей следующей статье…

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .