Реагировать на нарушение js-инварианта Хуки и функциональные компоненты - PullRequest
0 голосов
/ 19 сентября 2019

Я использую крючки и функциональные компоненты вместе.

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

Это дает мне следующеепроблема, даже если я поставил простое логическое условие, подобное этому, в качестве инвариантного условия:

invariant(false,..)

Ошибка:

Произошла перекрестная ошибка.React не имеет доступа к фактическому объекту ошибки в разработке.Смотрите .... для получения дополнительной информации.

Ссылка: codesandbox

Код:

import React, { useState } from "react";
import PropTypes from "prop-types";
import deburr from "lodash/deburr";
import Downshift from "downshift";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "../MyTextField/MyTextField";
import { Paper, MenuItem, Chip } from "@material-ui/core";

import invariant from "invariant";
import { isPlainObject } from "lodash";

function getStringLabelKey(labelKey) {
  return typeof labelKey === "string" ? labelKey : "label";
}

function getOptionLabel(option, labelKey) {
  //console.log("getOptionLabel", option, labelKey);

  if (option.paginationOption || option.customOption) {
    return option[getStringLabelKey(labelKey)];
  }

  let optionLabel;

  if (typeof option === "string") {
    optionLabel = option;
  }

  if (typeof labelKey === "function") {
    // This overwrites string options, but we assume the consumer wants to do
    // something custom if `labelKey` is a function.
    optionLabel = labelKey(option);
  } else if (typeof labelKey === "string" && isPlainObject(option)) {
    optionLabel = option[labelKey];
  }

  invariant(
    typeof optionLabel === "string",
    "One or more options does not have a valid label string. Check the " +
      "`labelKey` prop to ensure that it matches the correct option key and " +
      "provides a string for filtering and display."
  );

  return optionLabel;
}

function renderInput(inputProps) {
  const { InputProps, classes, ref, ...other } = inputProps;
  return (
    <span className={classes.noDisplay}>
      <TextField
        InputProps={{
          inputRef: ref,
          classes: {
            root: classes.inputRoot,
            input: classes.inputInput
          },
          ...InputProps
        }}
        {...other}
      />
    </span>
  );
}

renderInput.propTypes = {
  /**
   * Override or extend the styles applied to the component.
   */
  classes: PropTypes.object.isRequired,
  InputProps: PropTypes.object
};

function renderSuggestion(suggestionProps) {
  const {
    suggestion,
    itemProps,
    //selectedItem,
    renderMenuItemChildren
  } = suggestionProps;
  return (
    <MenuItem
      {...itemProps}
      key={suggestion.email}
      component="div"
      style={{
        fontWeight: 400
      }}
    >
      {getOptionLabel(suggestion, renderMenuItemChildren)}
    </MenuItem>
  );
}

IntegrationDownshift.defaultProps = {
  labelKey: "label",
  renderMenuItemChildren: "label",
  placeholder: "Search...",
  label: "Search",
  noFound: "No matches found.",
  fullWidth: false
};

renderSuggestion.propTypes = {
  highlightedIndex: PropTypes.oneOfType([
    PropTypes.oneOf([null]),
    PropTypes.number
  ]).isRequired,
  index: PropTypes.number.isRequired,
  itemProps: PropTypes.object.isRequired,
  selectedItem: PropTypes.string.isRequired,
  suggestion: PropTypes.shape({
    label: PropTypes.string.isRequired
  }).isRequired,
  fullWidth: PropTypes.bool
};

function getSuggestions(value, { options, showEmpty = false } = {}) {
  const inputValue = deburr(value.trim()).toLowerCase();
  const inputLength = inputValue.length;
  let el = inputLength === 0 && !showEmpty ? [] : options;
  return el;
}

const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1,
    height: 50
  },
  container: {
    flexGrow: 1,
    position: "relative"
  },
  paper: {
    position: "absolute",
    zIndex: 1,
    marginTop: theme.spacing(1),
    left: 0,
    right: 0
  },
  chip: {
    margin: theme.spacing(0.5, 0.25)
  },
  inputRoot: {
    flexWrap: "wrap"
  },
  inputInput: {
    width: "auto",
    flexGrow: 1
  },
  divider: {
    height: theme.spacing(2)
  },
  noDisplay: {
    "& .MuiInputBase-inputAdornedEnd": {
      display: "none !important"
    },
    "& .MuiChip-root": {
      //width: "100%",
      justifyContent: "flex-start !important"
    },
    "& .MuiChip-deleteIcon": {
      position: "absolute",
      right: "5px"
    }
  },
  myClass: {
    width: "100%",
    backgroundColor: "#ffccaa"
  }
}));

export default function IntegrationDownshift({
  options,
  onSearch,
  onChange,
  selectedItem,
  labelKey,
  renderMenuItemChildren,
  placeholder,
  label,
  noResult,
  noFound,
  fullWidth,
  backgroundColorChip,
  colorTextChip
}) {
  const [search, setSearch] = useState("");

  const classes = useStyles();
  function handleInputChange(event) {
    //console.log("handleInputChangeINT", event.target.value);
    onSearch(event.target.value);
  }

  function handleChange(event) {
    //console.log("handleChangeINT", event);
    onChange(event);
  }

  function invCheck() {
    console.log("label", options[0] && options[0].label);

    invariant(
      typeof options[0].label === "string",
      "One or more options does not have a valid label string. Check the " +
        "`labelKey` prop to ensure that it matches the correct option key and " +
        "provides a string for filtering and display."
    );
  }

  invCheck();

  return (
    <div className={classes.root}>
      <Downshift
        id="downshift-options"
        onChange={handleChange}
        selectedItem={selectedItem}
        //itemToString={item => (item ? item.value : search)}
        itemToString={item => search}
      >
        {({
          clearSelection,
          getInputProps,
          getItemProps,
          getLabelProps,
          getMenuProps,
          highlightedIndex,
          inputValue,
          isOpen,
          openMenu,
          selectedItem
        }) => {
          const { onBlur, onChange, onFocus, ...inputProps } = getInputProps({
            onChange: event => {
              //console.log("onChangeDINT", event.target.value);
              setSearch(event.target.value);
              if (event.target.value === "") {
                clearSelection();
              }
            },
            onFocus: openMenu,
            placeholder
          });

          return (
            <div className={classes.container}>
              {renderInput({
                fullWidth: true,
                classes,
                label,
                InputLabelProps: getLabelProps({ shrink: true }),
                InputProps: {
                  endAdornment: selectedItem !== null && (
                    <Chip
                      key={1}
                      tabIndex={-1}
                      label={getOptionLabel(selectedItem, labelKey)}
                      className={classes.chip}
                      onDelete={() => clearSelection()}
                      style={{
                        width: fullWidth && "100%",
                        backgroundColor: backgroundColorChip,
                        color: colorTextChip
                      }}
                    />
                  ),
                  onBlur,
                  onChange: event => {
                    handleInputChange(event);
                    onChange(event);
                  },
                  onFocus
                },
                inputProps
              })}

              <div {...getMenuProps()}>
                {isOpen ? (
                  <Paper className={classes.paper} square>
                    {getSuggestions(inputValue, {
                      options,
                      showEmpty: false
                    }).map((suggestion, index) =>
                      renderSuggestion({
                        suggestion,
                        index,
                        renderMenuItemChildren,
                        itemProps: getItemProps({
                          item: suggestion
                        }),
                        highlightedIndex,
                        selectedItem
                      })
                    )}
                    {inputValue.length > 0 &&
                      getSuggestions(inputValue, { options, showEmpty: true })
                        .length === 0 && (
                        <MenuItem
                          key={"noFound"}
                          component="div"
                          style={{
                            fontWeight: 400
                          }}
                          onClick={() => {
                            if (noResult !== undefined) noResult(true);
                          }}
                        >
                          {noFound}
                        </MenuItem>
                      )}
                  </Paper>
                ) : null}
              </div>
            </div>
          );
        }}
      </Downshift>
    </div>
  );
}

1 Ответ

0 голосов
/ 19 сентября 2019

Вы получаете эту ошибку, потому что пытаетесь получить доступ к:

options[0].label

, в то время как опции могут быть пустым массивом.

перед инвариантом, проверьте, больше ли длина опций, чем0:

if(options.length > 0) {
    invariant(
      typeof options[0].label === "string",
      "One or more options does not have a valid label string. Check the " +
        "`labelKey` prop to ensure that it matches the correct option key and " +
        "provides a string for filtering and display."
    );
}

Кстати, если вы откроете DevTools в CodeSandbox (f12 на chrome) и перейдете в консоль, вы увидите более информативное сообщение об ошибке

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