如何在组件末尾执行JavaScript文件?

时间:2019-06-19 18:15:54

标签: javascript reactjs

在我的html加载后,我无法加载导入的javascript文件。

我最近开始使用react进行编码,对此我很陌生。我在我的网站上包含一个图片库。每当我运行代码时,我都会收到此错误:

TypeError: Cannot read property 'querySelector' of null

据我了解,这是因为导入的“ homebasescript.js”首先运行,因此没有任何返回值。 如何更改代码以使其正常工作?我将非常感谢您的帮助!

Homebase.js

import React, { Component } from 'react';
import './Homebase.scss';
import Shuffle from 'shufflejs';
import Demo from './Homebasescript.js';


class homebase extends Component {
  render() {
    return (

      <section className="about-page">

      <div className="container-about">
        <div className="row-about">
          <div className="col-12@sm">
            <h1 className="about-title">Shuffle homepage demo</h1>
          </div>
        </div>
      </div>

      <div className="container-about">
        <div className="row-about">
          <div className="col-4@sm col-3@md">
            <div className="filters-group">
              <label htmlFor="filters-search-input" className="filter-label">Search</label>
              <input className="textfield filter__search js-shuffle-search" type="search" id="filters-search-input" />
            </div>
          </div>
        </div>

        <div className="row-about">
          <div className="col-12@sm filters-group-wrap">
            <div className="filters-group">
              <p className="filter-label">Filter</p>
              <div className="btn-group filter-options">
                <button className="btn btn--primary" data-group="space">Space</button>
                <button className="btn btn--primary" data-group="nature">Nature</button>
                <button className="btn btn--primary" data-group="animal">Animal</button>
                <button className="btn btn--primary" data-group="city">City</button>
              </div>
            </div>
            <fieldset className="filters-group">
              <legend className="filter-label">Sort</legend>
              <div className="btn-group sort-options">
                <label className="btn active">
                  <input type="radio" name="sort-value" value="dom" defaultChecked /> Default
                </label>
                <label className="btn">
                  <input type="radio" name="sort-value" value="title" /> Title
                </label>
                <label className="btn">
                  <input type="radio" name="sort-value" value="date-created" /> Date Created
                </label>
              </div>
            </fieldset>
          </div>
        </div>
      </div>

      <div className="container-about">
        <div id="grid" className="row-about my-shuffle-container-about">
          <div className="col-1@sm col-1@xs my-sizer-element"></div>
        </div>
      </div>

      </section>

    );
  };
};

export default homebase;

Homebasescript.js

import Shuffle from 'shufflejs';

class Demo {
  constructor(element) {
    this.element = element;
    this.shuffle = new Shuffle(element, {
      itemSelector: '.picture-item',
      sizer: element.querySelector('.my-sizer-element'),
    });

    // Log events.
    this.addShuffleEventListeners();
    this._activeFilters = [];
    this.addFilterButtons();
    this.addSorting();
    this.addSearchFilter();
  }

  /**
   * Shuffle uses the CustomEvent constructor to dispatch events. You can listen
   * for them like you normally would (with jQuery for example).
   */
  addShuffleEventListeners() {
    this.shuffle.on(Shuffle.EventType.LAYOUT, (data) => {
      console.log('layout. data:', data);
    });
    this.shuffle.on(Shuffle.EventType.REMOVED, (data) => {
      console.log('removed. data:', data);
    });
  }

  addFilterButtons() {
    const options = document.querySelector('.filter-options');
    if (!options) {
      return;
    }

    const filterButtons = Array.from(options.children);
    const onClick = this._handleFilterClick.bind(this);
    filterButtons.forEach((button) => {
      button.addEventListener('click', onClick, false);
    });
  }

  _handleFilterClick(evt) {
    const btn = evt.currentTarget;
    const isActive = btn.classList.contains('active');
    const btnGroup = btn.getAttribute('data-group');

    this._removeActiveClassFromChildren(btn.parentNode);

    let filterGroup;
    if (isActive) {
      btn.classList.remove('active');
      filterGroup = Shuffle.ALL_ITEMS;
    } else {
      btn.classList.add('active');
      filterGroup = btnGroup;
    }

    this.shuffle.filter(filterGroup);
  }

  _removeActiveClassFromChildren(parent) {
    const { children } = parent;
    for (let i = children.length - 1; i >= 0; i--) {
      children[i].classList.remove('active');
    }
  }

  addSorting() {
    const buttonGroup = document.querySelector('.sort-options');
    if (!buttonGroup) {
      return;
    }
    buttonGroup.addEventListener('change', this._handleSortChange.bind(this));
  }

  _handleSortChange(evt) {
    // Add and remove `active` class from buttons.
    const buttons = Array.from(evt.currentTarget.children);
    buttons.forEach((button) => {
      if (button.querySelector('input').value === evt.target.value) {
        button.classList.add('active');
      } else {
        button.classList.remove('active');
      }
    });

    // Create the sort options to give to Shuffle.
    const { value } = evt.target;
    let options = {};

    function sortByDate(element) {
      return element.getAttribute('data-created');
    }

    function sortByTitle(element) {
      return element.getAttribute('data-title').toLowerCase();
    }

    if (value === 'date-created') {
      options = {
        reverse: true,
        by: sortByDate,
      };
    } else if (value === 'title') {
      options = {
        by: sortByTitle,
      };
    }
    this.shuffle.sort(options);
  }

  // Advanced filtering
  addSearchFilter() {
    const searchInput = document.querySelector('.js-shuffle-search');
    if (!searchInput) {
      return;
    }
    searchInput.addEventListener('keyup', this._handleSearchKeyup.bind(this));
  }

  /**
   * Filter the shuffle instance by items with a title that matches the search input.
   * @param {Event} evt Event object.
   */
  _handleSearchKeyup(evt) {
    const searchText = evt.target.value.toLowerCase();
    this.shuffle.filter((element, shuffle) => {
      // If there is a current filter applied, ignore elements that don't match it.
      if (shuffle.group !== Shuffle.ALL_ITEMS) {
        // Get the item's groups.
        const groups = JSON.parse(element.getAttribute('data-groups'));
        const isElementInCurrentGroup = groups.indexOf(shuffle.group) !== -1;
        // Only search elements in the current group
        if (!isElementInCurrentGroup) {
          return false;
        }
      }
      const titleElement = element.querySelector('.picture-item__title');
      const titleText = titleElement.textContent.toLowerCase().trim();
      return titleText.indexOf(searchText) !== -1;
    });
  }
}

document.addEventListener('DOMContentLoaded', () => {
  window.demo = new Demo(document.getElementById('grid'));
});

错误:

TypeError: Cannot read property 'querySelector' of null

出现在我的“ Homebasescript.js”中。

1 个答案:

答案 0 :(得分:0)

是的,问题似乎在于尝试在const storage = require('@google-cloud/storage')(); const myBucket = storage.bucket('my-bucket'); const file = myBucket.file('my-file'); const contents = 'This is the contents of the file.'; file.save(contents, function(err) { if (!err) { // File written successfully. } }); //- // If the callback is omitted, we'll return a Promise. //- file.save(contents).then(function() {}); 事件侦听器中创建新的Demo实例。此事件将不考虑所有React组件的呈现,仅考虑实际HTML的初始加载(这可能是非常基本的,例如单个DOMContentLoaded)。因此,在触发div之后将不会渲染ID为"grid"的元素,这就是为什么该元素显示为DOMContentLoaded的原因。

在React项目中创建几乎所有东西的推荐方法是在其他组件中这样做。您可以将null实例的创建移到Demo类的生命周期方法中。但是,看起来您是在homebase类内部呈现了一些HTML,然后使用homebase类直接修改DOM来更改该HTML。

一种更好的方法是让Demo也是一个React组件,并使其以Demo的身份呈现div。这种直接操作DOM的方法类似于旧的jQuery呈现方式,并且使用React,您可以使用JavaScript提前决定要呈现的内容,而不是呈现然后进行修改。组件也可以在内部保持自己的状态,而不需要将所有内容存储在CSS类中。所有这些功能一起使代码更具可读性。

基本上,您会得到类似的东西:

"grid"

您提供的代码中有很多内容要讨论,但是希望本示例演示了一些如何在React中控制渲染的方法。我真的建议您浏览intro docs,因为它们很容易使用,您会学到很多东西。让我知道是否可以在这里澄清任何内容!