【问题标题】:React/Typescript Storybook not allowing objects to be importedReact/Typescript Storybook 不允许导入对象
【发布时间】:2022-01-09 21:31:42
【问题描述】:

无论出于何种原因,我的故事书设置都不喜欢对象导入。每次在对象上导入它的 undefined 并给我一个 TypeError。

对象作为 React 子级无效(发现:TypeError: Cannot read properties of undefined (reading 'news'))。如果您打算渲染一组子项,请改用数组。

我正在尝试导入 api.ts 的文件

interface Api {
  news: {
    getNews: Function;
  };
}

const api: Api = {
  news: {
    getNews: async (
      pageIndex?: number,
      pageSize?: number,
      featuredArticleId?: string,
      newsCategoryId?: string,
      type?: string,
      tag?: string,
    ) => {
      const { data }: AxiosResponse = await Axios.get(`${config.apiUrl}/news/listing`, {
        params: {
          newsCategoryId,
          pageIndex,
          pageSize,
          type,
          featuredArticleId,
          tag,
        },
      });
      return data;
    },
  },
};

export default api;

React 组件看起来像这样。当我注销 api 时,它总是未定义。

import * as React from 'react';
import { useEffect, useState, useRef } from 'react';
import Reveal from 'react-reveal/Reveal';
import { v4 as uuidv4 } from 'uuid';
import {
  Loader,
  Pagination,
  Dropdown,
  SectionHeading,
} from '../Common';
import { Container } from '../Helpers/Grid';
import FeaturedArticleCard from '../FeaturedNewsCard';
import NewsCard from '../NewsCard';
import NewsListProps from './type';
import NewsCardProps from '../NewsCard/type';
import getCurrentPage from '../Common/Pagination/getCurrentPage';
import api from '../../core/api/models';
import { NewsListApiResponse, Pagination as PaginationType } from '../../core/typings/api';
import { scrollIntoView, insertUrlParam } from '../../utilities/browser';
import './styles.scss';

console.log('api -> ', api);

interface FilterOption {
  displayText: string;
  value: string;
  selectedValue?: boolean;
}
// May need to come from the languages array
const defaultSelectedValue: string = 'all';

const NewsList = ({
  categories,
  featuredArticle,
  newsCategoryId,
  title,
  tag,
  noResultsText,
}: NewsListProps) => {
  const newsListItemsContainer = useRef<HTMLUListElement>(null);
  const [pageSize] = useState<number>(12);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [selectedFilter, setSelectedFilter] = useState<FilterOption>(categories[0]);
  const [items, setItems] = useState<NewsCardProps[]>();
  const [apiError, setApiError] = useState<string>();
  const [pagination, setPagination] = useState<PaginationType>();

  /**
   * Calls to news list api endpoint and updates state
   * accordingly
   * @param page
   */
  const fetchNews = async (page: number) => {
    const featuredArticleId = featuredArticle ? featuredArticle.id : undefined;
    const type = selectedFilter?.value !== defaultSelectedValue ? selectedFilter.value : undefined;
    setIsLoading(true);

    try {
      const data: NewsListApiResponse = await api.news.getNews(
        page,
        pageSize,
        featuredArticleId,
        newsCategoryId,
        type,
        tag,
      );
      setItems(data.items);
      setPagination(data.pagination);
      setIsLoading(false);
    } catch (error) {
      setApiError(error as string);
    }
  };

  /**
   * Pagination change action
   * @param page
   */
  const handlePage = (page: number) => {
    fetchNews(page);
    scrollIntoView(
      newsListItemsContainer?.current as HTMLUListElement,
      'smooth',
    );
  };

  /**
   * Updates the selected filter item from the jump list action
   * @param option
   */
  const filterByCategory = async (option: FilterOption) => {
    setSelectedFilter(option);
  };

  /**
   * Observes the selectedFilter state. If the state changes
   * and the value is not the default value it will call the
   * fetchNews method.
   */
  useEffect(() => {
    if (selectedFilter) {
      fetchNews(1);
      insertUrlParam('page', '1');
    }
  }, [selectedFilter]);

  /**
   * Inital actions on componentDidMount
   */
  useEffect(() => {
    // fetchNews(Number(getCurrentPage()));

    // We need to listen on popstate
    window.addEventListener('popstate', () => {
      fetchNews(Number(getCurrentPage()));
    }, false);
  }, []);

  return (
    <Container>
      <>
        {featuredArticle && (
          <div className="news-list-feature">
            <div className="news-list-feature__label">
              <h3>Featured</h3>
            </div>
            <Reveal effect="shortFadeInUp">
              <FeaturedArticleCard {...featuredArticle} />
            </Reveal>
          </div>
        )}

        <section className="news-list">
          {Boolean(!isLoading && items?.length) && (
            <div className="news-list__title">
              <SectionHeading text={title} />
            </div>
          )}

          <div className="news-list__container">
            {categories && (
              <div className="news-list__filters">
                <Dropdown
                  items={categories}
                  action={filterByCategory}
                  selectedValue={selectedFilter.value}
                  icon="icon-plus"
                  position="right"
                />
              </div>
            )}

            {apiError && (
              <div className="news-list__api-error">
                <h3>{apiError}</h3>
              </div>
            )}

            {Boolean(!items?.length && !isLoading) && (
              <div className="news-list__no-results">
                <h3>{noResultsText}</h3>
              </div>
            )}

            {isLoading && (
              <div className="news-list__loader">
                <Loader />
              </div>
            )}

            <ul ref={newsListItemsContainer} className="news-list__items">
              {Boolean(!isLoading && items?.length) && (
                <>
                  {items?.map((item, index) => (
                    <Reveal effect="shortFadeInUp" delay={index * 50}>
                      <li key={uuidv4()} className="news-list__item">
                        <NewsCard key={uuidv4()} {...item} />
                      </li>
                    </Reveal>

                  ))}
                </>
              )}
            </ul>

            {Boolean(pagination && items?.length) && (
              <nav className="news-list__pagination">
                <Pagination
                  totalItems={pagination?.totalItems as number}
                  currentPage={pagination?.currentPage as number}
                  itemsPerPage={pagination?.itemsPerPage as number}
                  handleClick={handlePage}
                />
              </nav>
            )}
          </div>
        </section>
      </>
    </Container>
  );
};

export default NewsList;

我猜这可能与我设置项目的方式有关?不知道发生了什么以及为什么,我只是在网上发现了死胡同。

【问题讨论】:

  • 你能展示一下你是如何在组件中使用它的吗?你在哪里访问news
  • setApiError(error as string); 不投射时错误是什么样子的?
  • 错误是否可能实际上是您尝试在组件中呈现为字符串的对象?
  • 不敢相信这是我什至没有意识到的问题。我整天都在看这个,仅此而已。非常感谢
  • 其实我在说谎 api 仍然未定义但现在没有出现 TypeError

标签: javascript reactjs typescript storybook


【解决方案1】:

我很确定您的组件看起来不像您在此处展示的那样。这实际上是一个重要的细节。

我预计它可能太长或太复杂。遵循最佳实践并保持组件简单总是好的。 ?

您可以从组件中返回什么:

  • 字符串
  • 数组
  • 反应组件

不好的例子:

const NewsList = ({
  data,
}: NewsListProps) => {
  return (
    <>
      {data}
    </>
  )
}

好例子:

const NewsList = ({
  data,
}: NewsListProps) => {
  return data.title
}
const NewsList = ({
  data,
}: NewsListProps) => {
  return [data.title]
}
const NewsList = ({
  data,
}: NewsListProps) => {
  return (
    <>
      {data.title}
    </>
  )
}

【讨论】:

  • 嗯?不知道你说的这一切是什么意思?我导出我的反应组件,但我将 api.ts 导入我的反应组件以供使用,但在我导入反应组件后它显示为未定义
  • 您可以编辑您的问题并显示您的组件的外观吗?
  • 现在已经为您完成了
猜你喜欢
  • 2022-07-09
  • 2021-12-29
  • 2020-11-14
  • 2021-03-03
  • 2017-11-09
  • 2020-07-28
  • 2018-08-17
  • 2019-10-23
  • 1970-01-01
相关资源
最近更新 更多