简体   繁体   English

React js 不变违规 Hooks 和 Function 组件

[英]React js invariant violation Hooks and Function Components

I'm using the Hooks and Function Components together.我将 Hooks 和 Function 组件一起使用。

Inside the Components I have to use invariant, but when I use it it gives me problems.在组件内部,我必须使用不变量,但是当我使用它时,它会给我带来问题。

It gives me the following problem, even if I put a simple boolean condition like this as an invariant condition:它给了我以下问题,即使我将这样的简单 boolean 条件作为不变条件:

invariant(false,..)

Error:错误:

A cross-origin error was thrown.引发了跨域错误。 React doesn't have access to the actual error object in development. React 无法访问开发中的实际错误 object。 See.... for more information.请参阅......了解更多信息。

Link: codesandbox链接: codesandbox

Code:代码:

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>
  );
}

You are getting this error because you are trying to access:您收到此错误是因为您尝试访问:

options[0].label

while options might be an empty array.而 options 可能是一个空数组。

before invariant, check if the length of options is greater than 0:在不变量之前,检查选项的长度是否大于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."
    );
}

Btw, if you open DevTools in CodeSandbox (f12 on chrome) and go to console, you will see a more informative error message顺便说一句,如果您在 CodeSandbox(chrome 上的 f12)和 go 中打开 DevTools 到控制台,您将看到一条信息更丰富的错误消息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM