【问题标题】:Why PropTypes failed with Value is undefined为什么 PropTypes 失败且 Value 未定义
【发布时间】:2020-02-05 19:32:58
【问题描述】:

我无法弄清楚为什么我在 React 中的函数组件中的原型会因此而失败:

`index.js:1 Warning: Failed prop type: The prop `profiles` is marked as required in `Home`, but its value is `undefined`.` 

应用程序运行良好,配置文件已定义,我正在使用带有钩子的 React-redux,这可能会导致问题,因为我实际上不知道如何使 PropTypes 工作

出现这种情况的我的家:

import React, { useEffect, useState } from "react";
import { Row, Col, Jumbotron, Container, Image } from "react-bootstrap";
import { ProfileMiddleware } from "../Store/Middleware";
import { PropTypes } from "prop-types";
import { useDispatch, useSelector } from "react-redux";
import { USERNAME } from "../Services/constAPI";
import Experiences from "../Components/Experiences/Experiences";
import { Spinner } from "../Components/Spinner/Spinner.js";

const Home = () => {
  const dispatch = useDispatch();
  const { profiles, displaySpinner } = useSelector(state => ({
    profiles: state.ProfileReducer.profiles,
    displaySpinner: state.ProfileReducer.displaySpinner
  }));

  useEffect(() => {
    dispatch(ProfileMiddleware.getOneProfile(USERNAME));
  }, [dispatch]);

  return !profiles.object ? (
    <>

      <Jumbotron>
        <Container>
          <Row>
            <Col md={6}>
              <Image src={profiles.imageUrl} alt="profile" roundedCircle />
            </Col>
            <Col md={6}>
              <h1>{profiles.firstname + " " + profiles.surname}</h1>
              <h4>{profiles.title}</h4>
              <h5>{profiles.area}</h5>
              <p>{profiles.email}</p>
              <p>{profiles.bio}</p>
            </Col>
          </Row>
          <Spinner displaySpinner={displaySpinner} />
        </Container>
      </Jumbotron>
      <Experiences />
    </>
  ) : (
    <h3 className="red-text mt-5">The profile is not available</h3>
  );
};

Home.propTypes = {
  profiles: PropTypes.object.isRequired
};

export default Home;

我使用 Redux 时的 reducer

import { ProfileActions } from "../Actions";

function ProfileReducer(
  state = {
    profiles: {},
    displaySpinner: false
  },
  action
) {
  console.log("data in action", action.data);
  console.log("Action type", action.type);

  switch (action.type) {
    case ProfileActions.GET_ONE_PROFILE:
      return {
        ...state,
        displaySpinner: true
      };
    case ProfileActions.GET_ONE_PROFILE_SUCCESS:
      return {
        ...state,
        profiles: action.data,
        displaySpinner: false
      };
    default:
      return state;
  }
}

export default ProfileReducer;

如有必要,我可以显示其他内容,但 APP 可以工作,但 PropTypes 说配置文件未定义,我无法理解。

【问题讨论】:

  • 您似乎没有将任何道具传递给Home。你为什么要这样做,然后又试图要求 profiles 道具?
  • 我必须使用 PropTypes 检查配置文件是否为 obj 我正在尝试找到解决方案以使其正常工作。如果您知道解决方案,请分享答案

标签: reactjs react-redux react-hooks


【解决方案1】:

您没有向Home 传递任何道具。如果你是,它看起来像

const Home = (props) => {

相反,您会从您的 redux 商店获得 profiles。所以简单地改变

Home.propTypes = {
  profiles: PropTypes.object.isRequired
};

Home.propTypes = {};

【讨论】:

    【解决方案2】:

    profiles 不是传递给Home 的道具。 Home 组件的 proptypes 应该被删除。

    【讨论】:

      【解决方案3】:

      你在 cmets 里说:

      我必须使用 PropTypes 检查配置文件是否为 obj 我正在尝试找到解决方案以使其正常工作。如果您知道解决方案,请分享答案

      如果您能保证在您的selector 中做到这一点,那就更好了,但在组件中有办法做到这一点。

      所以让我们对您的代码进行 sn-p 操作:

      const Home = () => {
        const dispatch = useDispatch();
        const { profiles, displaySpinner } = useSelector(state => ({
          profiles: state.ProfileReducer.profiles,
          displaySpinner: state.ProfileReducer.displaySpinner
        }));
      
        // note Array's and other variable are also objects
        // so we need to do a special check
        const isProfilesAnObject = profiles && profiles.constructor.name === 'Object';
        // created outside of `every` loop, and ternary shortcut to empty array
        const profilesKeys = isProfilesAnObject ? Object.keys(profiles) : [];
        // check that profileKeys is a subset of REQUIRED_KEYS
        // n.b. you need to define this somewhere
        const isProfilesCorrect = REQUIRED_KEYS.every(requiredKey => profileKeys.includes(requiredKey))
      
        useEffect(() => {
          dispatch(ProfileMiddleware.getOneProfile(USERNAME));
        }, [dispatch]);
      
        return isProfilesCorrect ?
      

      这应该像下面这样工作:

      const checkProfiles = profiles => {
        const REQUIRED_KEYS = ['a', 'b', 'z', 'y']
        const isProfilesAnObject = profiles && profiles.constructor.name === 'Object';
        // created outside of `every` loop, and ternary shortcut to empty array
        const profilesKeys = isProfilesAnObject ? Object.keys(profiles) : [];
        // check that profileKeys is a subset of REQUIRED_KEYS
        // n.b. you need to define this somewhere
        const isProfilesCorrect = REQUIRED_KEYS.every(requiredKey => profilesKeys.includes(requiredKey))
        return isProfilesCorrect;
      }
      const [profile1, profile2, profile3] = [{
        'a': '10'
      }, {
        a: '10', b: '20', z: '260', y: '250'
      },{
        a: '10', b: '20', extra_key: 'I\'m being a little bit extra', z: '260', y: '250'
      }]
      
      // missing keys
      console.log(`profile1 (with keys ${Object.keys(profile1)}) is: ${checkProfiles(profile1) ?'correct': 'incorrect'}`);
      
      // just the right number of keys
      console.log(`profile2 (with keys ${Object.keys(profile2)}) is: ${checkProfiles(profile2) ?'correct': 'incorrect'}`);
      
      // doesn't check if there aren't extra keys
      console.log(`profile3 (with keys ${Object.keys(profile3)}) is: ${checkProfiles(profile3) ?'correct': 'incorrect'}`);

      还有invariant,您可以使用它进行更严格的检查。如果某些东西是假的,它会抛出一个错误,所以你可以在那里进行精确检查,如下所示。

      (注意忽略我必须定义的 processmodule 对象,并注意我使用 tiny-invariant 方便)

      const checkProfiles = profiles => {
        const REQUIRED_KEYS = ['a', 'b', 'z', 'y']
        const isProfilesAnObject = profiles && profiles.constructor.name === 'Object';
        // created outside of `every` loop, and ternary shortcut to empty array
        const profilesKeys = isProfilesAnObject ? Object.keys(profiles) : [];
        // check that profileKeys is a subset of REQUIRED_KEYS
        // n.b. you need to define this somewhere
        const isProfilesCorrect = REQUIRED_KEYS.every(requiredKey => profilesKeys.includes(requiredKey))
        // really need to make sure 'z' exists and is a string
        invariant(typeof profiles.z === 'string', `Profiles is expected to have a key 'z' that is a string, but found ${JSON.stringify(profiles.z)}`)
        return isProfilesCorrect;
      }
      
      
      console.log(checkProfiles({
        a: 10,
        z: 'valid'
      }))
      try {
        checkProfiles({
          a: 10,
          z: 20
        });
      } catch (e) {
        console.error(e);
      }
      <script>
        var process = {
          env: 'production'
        }
        var module = {
          exports: {}
        }
      </script>
      <script src="https://cdn.jsdelivr.net/npm/tiny-invariant@1.2.0/dist/tiny-invariant.cjs.min.js">
      </script>

      其中任何一个都应该起作用,具体取决于您需要确保变量存在且正确的程度

      【讨论】:

        【解决方案4】:

        其实你没有通过props,注意:

        const Home = () => {
        

        你应该在函数组件的开头写const Home = props =&gt; {或破坏props,如下所示:

        const Home = ({ profiles }) => {
        

        然后在函数组件的执行上下文中使用它。此外,您可以为您的道具设置默认值,如下所示:

        const Home = ({ profiles = 'something' }) => {
        

        'something' 是一个示例,它可以是所有内容,或者在函数组件声明的末尾写如下:

        Home.defaultProps = {
          profiles: 'something'
        };
        

        我希望它对您有所帮助,但您当然应该多阅读ReactJS docs

        【讨论】:

        • 我阅读了文档,您能否指出一个带有函数组件的示例,因为这些示例带有类组件并且有点不同。如果我在函数本身中定义类似的道具或配置文件,那么第二件事也是我必须做的,因为我收到一个错误,因为它永远不会被读取。我可以举一个完整的例子吗?会很有帮助的
        • 但是我不能这样做,因为反应不允许它stackoverflow.com/a/54814692/7882685
        • 用户没有使用道具作为个人资料,所以告诉他们如何这样做是没有实际意义的。
        猜你喜欢
        • 1970-01-01
        • 2018-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-08
        • 2014-04-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多