React.js and Isotope.js

亡梦爱人 提交于 2019-12-03 16:32:36

问题


I'm checking out React.js and trying to figure out how this library can work together with Isotope.js. The documentation of React says that it plays nicely with other libraries, but using it with library that changes DOM on its own seems like no sense of using React.

Can someone explain to me, how can I take advantage of React in my webapp that uses Isotope.js as layout ?


回答1:


You can manipulate the dom directly inside React. This permits to integrate existing JS libraries or for custom needs not handled well by React.

You can find an exemple here:

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

And here's what it looks like:


The problem with integration of React and a library like Isotope is that you will end up having 2 different libraries trying to update the same dom subtree. As React work with diffs, it kind of assumes that it is alone modyfing the dom.

So the idea could be to create a React component that will render only one time, and will never update itself. You can ensure this with:

shouldComponentUpdate: function() { 
    return false; 
}

With this you can:

  • Use React to generate your isotope item html elements (you can also create them without React)
  • On componentDidMount, initialize isotope on the dom node mounted by React

And that's all. Now React will never update this part of the dom again, and Isotope is free to manipulate it like it wants to without interfering with React.

In addition, as far as I understand, Isotope is not intented to be used with a dynamic list of items so it makes sense to have a React component that never updates.




回答2:


Here's a working version with Masonry, you should find it easy enough to port to Isotope (or use Masonry :)) http://jsfiddle.net/emy7x0dc/1/.

Here's the crux of the code that makes it work (and allow React to do its job).

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's 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:


Here's an updated version of the above code posted by James:

If you're using webpack, remember to modify your webpack config for working with Isotope.

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

// Container for isotope grid
class ItemGrid extends PureComponent {
    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;

Usage:

Just pass the items you want to keep inside isotope into the ItemGrid component as children:

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

Alternatives

If you can, consider using react-masonry-component.




回答4:


You need to create new Isotope object on componentDidMount and reload items on componentDidUpdate.

Use my mixin to figure it out :)




回答5:


I got Isotope working in React by following Amith's quick tutorial at this link. The key was to address filtering within the onClick function:

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>
    )
  }
}

It now works exactly like it did on my static jQuery site. If you want the filter buttons to change appearance when active you can simply update local state in the onFilterChange function and render the buttons based on that.




回答6:


My solution with useState and useEffect hooks, also works with dynamically generated filter keys and items. The trick is to initialize Isotope after the component is mounted, and call its "arrange" method everytime the filter keyword changes. You can achieve the same with componentDidMount and componentDidUpdate in a class component.

Demo: 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>
    </>
  );
};


来源:https://stackoverflow.com/questions/25135261/react-js-and-isotope-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!