javascript - 无法读取 null 的属性 'appendChild' - Tiny Slider React

标签 javascript reactjs spfx

这里是 React 新手,我的 TinySlider 组件遇到问题。每次我在用户界面中更新轮播中可以出现的帖子数量时,我都会在每次需要修复的其他更新中收到此错误:

Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null

如果我删除 <TinySlider settings={...settings}></Tinyslider>我没有收到此错误。

我已经尝试过:{ renderProfilesCarousel ? renderProfilesCarousel : '' }里面<tinySlider>但这不起作用。

知道我能在这里做什么吗?现在很坚持这个。

// React
import * as React from 'react';
// Styling
import styles from './LinkedIn.module.scss';
// Importing the props
import { ILinkedInProps } from './ILinkedInProps';
// Importing the state
import { ILinkedInState } from './ILinkedInState';
// Removes special characters 
import { escape } from '@microsoft/sp-lodash-subset';
// Library for making http requests
import axios from 'axios';
// Library for creating unique ids
import shortid from 'shortid';
// Fabric UI elements
import {
  DocumentCard,
  DocumentCardPreview,
  DocumentCardType,
  DocumentCardDetails,
  DocumentCardTitle,
  IDocumentCardPreviewProps
} from 'office-ui-fabric-react/lib/DocumentCard';
import { ImageFit, Image } from 'office-ui-fabric-react/lib/Image';
// Sort array
import sortBy from 'sort-array';
import TinySlider from "tiny-slider-react";
import { SPComponentLoader } from '@microsoft/sp-loader';
import "./styles.scss";

// LinkedIn Component Class
export default class LinkedIn extends React.Component<ILinkedInProps, ILinkedInState> {

  // State needed for the component
  constructor(props) {
    super(props);
    this.state = {
        profiles: [],
        isLoading: true,
        errors: null
    };
    SPComponentLoader.loadCss('//cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.2/tiny-slider.css');
  }

  // This function runs when component is first renderd
  public componentDidMount() {
    this.getProfiles();
  }

  // This function runs when props that have changed have been passed in
  public componentDidUpdate(prevProps) {
    if ( prevProps.listName !== this.props.listName || prevProps.postCount ! == this.props.postCount )
    {
      this.renderProfile();
    }
  }

  // Grabs LinkedIn profiles - This service runs once a day
  private getProfiles() {
    let companyNameCreate: string;
    let backUpImageCreate: string;
    axios
      .get(
        "https://cors-anywhere-spfx.herokuapp.com/" +
        "https://cache1.phantombooster.com/YRrbtT9qhg0/KJhwG7zo0zPE5zc9Eehn6Q/result.json"
      )
      .then(response => {
      this.setState({
        profiles: response.data.filter(d => d.postContent).map(post => {
          if (post.profileUrl == 'https://www.linkedin.com/company/')
          {
            companyNameCreate = 'company';
            backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQEbfV4VNvsJyg/company-logo_100_100/0?e=1587600000&v=beta&t=CX_s-ekYNn0TnXANeftQkLZ9jIvMW7PtDTLLcHcu9wY'
          }
          else if (post.profileUrl == 'https://www.linkedin.com/company/1')
          {
            companyNameCreate = 'company';
            backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQG_Pr1cDaGfdA/company-logo_200_200/0?e=1587600000&v=beta&t=C0fWkjbO2Elth8K4pG4i_kzwlu8dvQvN1Ws-yKGxxP4'
          }
          else if (post.profileUrl == 'https://www.linkedin.com/company/2')
          {
            companyNameCreate = 'company';
            backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQHdub-mnNwSNg/company-logo_100_100/0?e=1587600000&v=beta&t=druqo_O5gB5cExttREUlSdGnJhr4Wx2-PCjshJ0K0fI'
          }
          else if (post.profileUrl == 'https://www.linkedin.com/company/3')
          {
            companyNameCreate = 'company';
            backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQEUKGn5i1EnQA/company-logo_100_100/0?e=1587600000&v=beta&t=uwE3CUaodiqmW2K3a3Hm57QDIDlMvrmfmoHDvlGnzuY'
          }
          else if (post.profileUrl =='https://www.linkedin.com/company/4')
          {
            companyNameCreate = 'company';
            backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQGYqqxF43Rfdg/company-logo_200_200/0?e=1587600000&v=beta&t=4hmLzdbkjk_hL3rwonWgTbUF1V-itkyBEfLBh85G7_k'
          }
          else if (post.profileUrl == 'https://www.linkedin.com/company/5')
          {
            companyNameCreate = 'company';
            backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4E0BAQHsNKLawvW7zg/company-logo_100_100/0?e=1587600000&v=beta&t=26Otro4T3q90GznPxXX6n3oPTYYWIgzodOIask0enu4'
          }
          return {
            ...post,
            companyName: companyNameCreate,
            backUpImage:  backUpImageCreate
          }
        })
      });
    })
    // Error catching
    .catch(errors => this.setState({ errors, isLoading: false }));
  }

  // Creates the renderd layout of the LinkedIn profile
  private async renderProfile() {
    let filter: string = '';
    // Works out which profile to display
    switch (this.props.listName) {
      case "abfi":
        filter = 'https://www.linkedin.com/company/1';
        break;
      case 'abEnzymes':
        filter = 'https://www.linkedin.com/company/2';
        break;
      case 'abitec':
        filter = 'https://www.linkedin.com/company/3';
        break;
      case 'ohly':
        filter = 'https://www.linkedin.com/company/4';
        break; 
      case 'pgpi':
        filter = 'https://www.linkedin.com/company/5';
        break;
      case 'spiPharma':
        filter = 'https://www.linkedin.com/company/6';
        break;
      case 'all': 
        filter = 'Post';
        break;
      default:
        filter = 'https://www.linkedin.com/company/1';
        return filter;
    }

    // Grabs the array of objects
    let { profiles } = this.state;

    const slotOrder = [
      "", "1h","2h","3h","4h","5h","6h","7h","8h","9h","10h","11h","12h", "13h","14h","15h","16h","17h","18h","19h","20h","21h","22h", "23h", "2d", "3d", "4d", "5d", "6d", "1w", "2w", "3w", "1mo", "2mo", "3mo", "4mo", "5mo", "6mo", "7mo", "8mo", "9mo", "10mo", "11mo", "1yr", "2yr"
    ];

    // Select only the needed objects from the array
    let selectedProfile;

    // Display all posts 
    if (this.props.listName !== 'all') {
      selectedProfile = profiles.filter(profile => profile.profileUrl == filter);
    } else {
      selectedProfile = profiles.filter(profile => profile.action == filter);
    }

    selectedProfile = sortBy(selectedProfile, "postDate", { postDate: slotOrder })
    selectedProfile = selectedProfile.splice(0, this.props.postCount)

    // Renders the selected profile
    let renderProfilesCarousel = selectedProfile.map(profile => {

      // If LinkedIn post has no image, then add a fallback!
      if (profile.imgUrl == "" || profile.imgUrl == null ) {
        profile.imgUrl = profile.backUpImage;
      }

      // Removes hashtag line from posts
      profile.postContent = profile.postContent.replace(/hashtag/g, '');

      return(
        <div className={styles.linkedInContainerCarousel} style={{ position: "relative" }} key={shortid.generate()}>
          <a href={profile.postUrl} target="_blank" data-interception="off"> 
              <DocumentCard
                aria-label={profile.postContent}
                className={styles.linkedInDocCard}
              > 
                { profile.imgUrl && 
                  <Image
                    src={profile.imgUrl}
                    imageFit={ImageFit.cover}
                    height={168}
                  />
                }
                <DocumentCardTitle
                  title={profile.postContent}
                  shouldTruncate={true}
                />
                <p className={ styles.linkedInCompany}>{profile.companyName}</p>
                <p className={ styles.linkedInLikes}>{`Likes: ${profile.likeCount}`}</p>
              </DocumentCard>
          </a>
        </div>
      )
    });

    // Renders the selected profile
    let renderProfiles = selectedProfile.map(profile => {

      // If LinkedIn post has no image, then add a fallback!
      if (profile.imgUrl == "" || profile.imgUrl == null ) {
        profile.imgUrl = profile.backUpImage;
      }

      let previewProps: IDocumentCardPreviewProps = {
        previewImages: [
          {
            name: profile.postContent,
            previewImageSrc: profile.imgUrl,
            iconSrc: null,
            imageFit: ImageFit.cover,
            height: 110,
            width: 110
          }
        ]
      };

      return(
        <div className={styles.linkedInContainer} style={{ position: "relative" }} key={shortid.generate()}>
          <a href={profile.postUrl} target="_blank" data-interception="off"> 
              <DocumentCard
                aria-label={profile.postContent}
                className={styles.linkedInDocCard}
                type={DocumentCardType.compact}
              > 
                { profile.imgUrl && 
                  <DocumentCardPreview {...previewProps} />
                }
                <DocumentCardDetails>
                <DocumentCardTitle
                  title={profile.postContent}
                  shouldTruncate={true}
                />
                  <p className={ styles.linkedInCompany}>{profile.companyName}</p>
                  <p className={ styles.linkedInLikes}>{`Likes: ${profile.likeCount}`}</p>
                </DocumentCardDetails>
              </DocumentCard>
          </a>
        </div>
      )
    });

    let settings: any;

    if (this.props.toggleInfoHeaderValue == true )
    {
      settings = {
        lazyload: true,
        nav: false,
        mouseDrag: false,
        items: 1,
        swipeAngle: false,
        speed: 400,
        autoplay: false,
        axis: "horizontal",
        autoHeight: false,
        rewind: true,
        fixedWidth: false
      };
    }
    else
    {
      settings = {
        lazyload: true,
        nav: false,
        mouseDrag: false,
        items: 3,
        swipeAngle: false,
        speed: 400,
        autoplay: false,
        axis: "vertical",
        autoHeight: false,
        rewind: true,
        fixedWidth: false
      };
    };

    if (this.props.toggleInfoScrollValue) {
      settings.autoplay = true;
    } else {
      settings.autoplay = false;
    }

    if (this.props.toggleInfoHeaderValue == true ) {
      return(
        <div>
            <TinySlider settings={settings}>
               {renderProfilesCarousel}
            </TinySlider>
        </div>
      )
    } else {
      return (

        <div className={styles.upArrows}>
            <TinySlider settings={settings}>
               {renderProfiles}
            </TinySlider>
        </div>
      )
    }   

  } 

  // Renders to the browser
  public render(): React.ReactElement<ILinkedInProps> {

    return (
      <div className={ styles.linkedIn }>
        <div className={ styles.container }>
          <p className={ styles.title }>{escape(this.props.description)}</p>
          <div>
            { this.renderProfile() }
          </div>
        </div>
      </div>
    );
  }
} 

enter image description here

完整错误: enter image description here

最佳答案

你可以试试这个吗

{ renderProfilesCarousel ? renderProfilesCarousel : <span></span> }

React 喜欢 elements ,我不确定它如何处理 ''

编辑编辑:

我认为您需要将实际的 JSX.Element 从 renderProfile() 方法中移出。 React 不会将其视为子元素。

所以我添加了两个新项目来声明(我猜您会需要三个,其中一个也用于 renderProfilesCarousel):

settings?: any;
renderProfiles?: JSX.Element[];

然后我在 renderProfile() 方法的底部执行了此操作:


        /* if (this.props.toggleInfoHeaderValue == true) {
            return (
                <div>
                    <TinySlider settings={settings}>
                        {renderProfilesCarousel}
                    </TinySlider>
                </div>
            )
        } else {
            return (

                <div /* className={styles.upArrows} >
                    <TinySlider settings={settings}>
                        {renderProfiles}
                    </TinySlider>
                </div>
            )
        } */
        console.log(renderProfiles.length);
        this.setState({
            settings: settings,
            renderProfiles: renderProfiles,
            isLoading: false
        })

然后,在将实际渲染返回给浏览器时,我放置实际的 JSX.Element:

    // Renders to the browser
    public render(): React.ReactElement<ILinkedInProfilesProps> {
        const {settings, renderProfiles} = this.state;
        const theRenderProfileJsxElement: JSX.Element = 
            <div /* className={styles.upArrows} */>
                <TinySlider settings={settings}>
                    {renderProfiles}
                </TinySlider>
            </div>;
        return (
            <div /* className={styles.linkedIn} */>
                <div /* className={styles.container} */>
                    <p /* className={styles.title} */>{escape(this.props.description)}</p>
                    <div>
                        {this.state.isLoading === false &&
                            theRenderProfileJsxElement
                        }
                    </div>
                </div>
            </div>
        );
    }

我使用了 isLoading 的状态来阻止轮播在完成所有逻辑并从上面加载之前加载。

还有!如果您的浏览器上没有 React Dev Tools,那么您需要它!

enter image description here

我可以看到组件轮播,但我没有为toggleInfoHeaderValue 执行if 逻辑。让我们看看这是否有效?

关于javascript - 无法读取 null 的属性 'appendChild' - Tiny Slider React,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59768586/

相关文章:

javascript - 使用 Vue 数据包含 CSS 元素

javascript - 单击 Javascript Alert 中的按钮在新窗口中打开弹出窗口

javascript - React 如何从 json 数据以表单形式呈现嵌套下拉列表?

javascript - 将计时器转换为 react

javascript - 带有 React 组件加载图像的 SharePoint 框架

javascript - 协助 javascript regexp 替换字符串

javascript - 不允许加载本地资源 : Ionic3(ios)

css - 无法让图像成为我想要的宽度

在共享点页面上加载 SPFX Webpart 之前调用 Javascript

node.js - 无法使用 webpack 加载 spfx 解决方案的 sp-loader