React.js и Isotope.js

Я проверяю React.js и пытаюсь выяснить, как эта библиотека может работать вместе с Isotope.js. В документации React говорится, что он отлично работает с другими библиотеками, но использование его с библиотекой, которая меняет DOM самостоятельно, похоже, не имеет смысла использовать React.

Может кто-нибудь объяснить мне, как я могу использовать React в моем webapp, который использует Isotope.js как макет?

Ответ 1

Вы можете манипулировать dom непосредственно внутри React. Это позволяет интегрировать существующие JS-библиотеки или для пользовательских запросов, которые не обрабатываются React.

Здесь вы можете найти пример:

https://github.com/stample/gulp-browserify-react-phonegap-starter/blob/master/src/js/home/homeComponents.jsx#L22

И вот как это выглядит:

image


Проблема с интеграцией React и библиотеки, такой как Isotope, заключается в том, что у вас будет две разные библиотеки, пытающиеся обновить одно и то же dom поддерева. Поскольку React работает с diffs, он предполагает, что он один modyfing dom.

Таким образом, идея может заключаться в создании компонента React, который будет отображаться только один раз и никогда не будет обновляться. Вы можете обеспечить это с помощью

shouldComponentUpdate: function() { 
    return false; 
}

С помощью этого вы можете:

  • Используйте React для генерации ваших элементов html элементов изотопа (вы также можете создавать их без React)
  • В componentDidMount инициализируйте изотоп на dom node, установленный React

И это все. Теперь React никогда не будет обновлять эту часть dom снова, и Isotope может свободно манипулировать им, как хочет, не вмешиваясь в React.

Кроме того, насколько я понимаю, Isotope не намерен использоваться с динамическим списком элементов, поэтому имеет смысл иметь компонент React, который никогда не обновляется.

Ответ 2

Здесь рабочая версия с масонством, вам должно быть достаточно легко отправить в Isotope (или использовать Masonry:)) http://jsfiddle.net/emy7x0dc/1/.

Вот суть кода, который заставляет его работать (и позволяет React выполнять свою работу).

var Grid = React.createClass({
    displayName: 'Grid',

    getInitialState: function(){
        return {
            masonry: null
        }
    },

    // Wrapper to layout child elements passed in
    render: function () {
        var children = this.props.children;
        return (
            <div className="grid">
                {children}
            </div>
        );
    },

    // When the DOM is rendered, let Masonry know what changed
    componentDidUpdate: function() {
        if(this.state.masonry) {
            this.state.masonry.reloadItems();
            this.state.masonry.layout();
        }
    },

    // Set up Masonry
    componentDidMount: function() {
        var container = this.getDOMNode();
        if(!this.state.masonry) {
            this.setState({
                masonry: new Masonry( container )
            });
        } else {
            this.state.masonry.reloadItems();
        }
    }
});

Ответ 3

Вот обновленная версия вышеприведенного кода, опубликованного Джеймсом:

Если вы используете веб-пакет, не забудьте изменить конфигурацию веб-пакета для работы с изотопом.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Isotope from 'isotope-layout';

// Container for isotope grid
class ItemGrid extends Component {
    constructor(props) {
        super(props);
        this.state = { isotope: null };
    }

    render() {
        return(
            <div className="item-grid">
                {this.props.children}
            </div>
        )
    }

    // set up isotope
    componentDidMount() {
        const node = ReactDOM.findDOMNode(this);
        if (!this.state.isotope) {
            this.setState({
                isotope: new Isotope( node )
            });
        } else {
            this.state.isotope.reloadItems();
        }
    }

    // update isotope layout
    componentDidUpdate() {
        if (this.state.isotope) {
            this.state.isotope.reloadItems();
            this.state.isotope.layout();
        }
    }
}

export default ItemGrid;

Использование:

Просто передайте элементы, которые вы хотите сохранить внутри изотопа, в компонент ItemGrid как дочерние:

<ItemGrid>
    {data.map((object) => {
        return <Item
            key={object._id}
            name={object.name}
            imageUrl={object.imageUrl} />
    })}
</ItemGrid>

Альтернативы

Если вы можете, рассмотрите возможность использования response-masonry-component.

Ответ 4

Вам нужно создать новый объект Isotope на компонентеDidMount и перезагрузить элементы в компонентеDidUpdate.

Используйте mixin, чтобы понять:

Ответ 5

Я научился работать с Изотопом в React, следуя краткому руководству Амит по этой ссылке. Ключ должен был обратиться к фильтрации внутри функции onClick:

class Parent extends Component {
  constructor(props) {
    super(props);
    this.onFilterChange = this.onFilterChange.bind(this);
  }

  // Click Function
  onFilterChange = (newFilter) => {
    if (this.iso === undefined) {
      this.iso = new Isotope('#filter-container', {
        itemSelector: '.filter-item',
        layoutMode: "fitRows"
      });
    }
    if(newFilter === '*') {
      this.iso.arrange({ filter: '*' });
    } else {
      this.iso.arrange({ filter: '.${newFilter}' });
    }
  }

  render() {
    return(
      // Filter Buttons
      <ul id="portfolio-flters">
        <li data-filter="*" onClick={() => {this.onFilterChange("*")}}>All</li>
        <li data-filter="filter-one" onClick={() => {this.onFilterChange("filter-one")}}>One</li>
        <li data-filter="filter-two" onClick={() => {this.onFilterChange("filter-two")}}>Two</li>
      </ul>

      // Isotope Grid & items
      <div id="filter-container">
        <div className='filter-item filter-one'>
          // Item Content
        </div>
        <div className='filter-item filter-two'>
          // Item Content
        </div>
      </div>
    )
  }
}

Теперь он работает точно так же, как и на моем статическом сайте jQuery. Если вы хотите, чтобы кнопки фильтра изменяли внешний вид, когда они активны, вы можете просто обновить локальное состояние в функции onFilterChange и визуализировать кнопки на основе этого.

Ответ 6

Мое решение с хуками useState и useEffect также работает с динамически генерируемыми ключами и элементами фильтра. Хитрость заключается в том, чтобы инициализировать Isotope после монтирования компонента и вызывать его метод "range "при каждом изменении ключевого слова фильтра. Вы можете добиться того же с помощью componentDidMount и componentDidUpdate в компоненте класса.

Демо: https://codepen.io/ilovepku/pen/zYYKaYy

const IsotopeReact = () => {
  // store the isotope object in one state
  const [isotope, setIsotope] = React.useState(null);
  // store the filter keyword in another state
  const [filterKey, setFilterKey] = React.useState("*");

  // initialize an Isotope object with configs
  React.useEffect(() => {
    setIsotope(
      new Isotope(".filter-container", {
        itemSelector: ".filter-item",
        layoutMode: "fitRows"
      })
    );
  }, []);

  // handling filter key change
  React.useEffect(
    () => {
      if (isotope) {
        filterKey === "*"
          ? isotope.arrange({ filter: '*' })
        : isotope.arrange({ filter: '.${filterKey}' });
      }
    },
    [isotope, filterKey]
  );

  return (
    <>
      <ul>
        <li onClick={() => setFilterKey("*")}>Show Both</li>
        <li onClick={() => setFilterKey("vege")}>Show Veges</li>
        <li onClick={() => setFilterKey("fruit")}>Show Fruits</li>
      </ul>
      <hr />
      <ul className="filter-container">
        <div className="filter-item vege">
          <span>Cucumber</span>
        </div>
        <div className="filter-item fruit">
          <span>Apple</span>
        </div>
        <div className="filter-item fruit">
          <span>Orange</span>
        </div>
        <div className="filter-item fruit vege">
          <span>Tomato</span>
        </div>
      </ul>
    </>
  );
};