React.js and Isotope.js

前端 未结 8 1722

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 libra

相关标签:
8条回答
  • 2020-12-30 03:44

    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();
            }
        }
    });
    
    0 讨论(0)
  • 2020-12-30 03:49

    Updated Hubert's answer to modern react with Hooks.

    import React, { useEffect, useRef, useState } from 'react';
    import Isotope from 'isotope-layout';
    
    function IsoContainer (props) {
        const isoRef = useRef();
        const [isotope, setIsotope] = useState(null);
    
        useEffect(() => {
            if (isotope)
                isotope.reloadItems();
            else
                setIsotope(new Isotope( isoRef.current ));
        })
    
        return (
            <div ref={isoRef}>
                {props.children}
            </div>
        )
    }
    
    export default IsoContainer
    

    Edit: Coming back to Isotope after not using for a few months. Using a container component as above isn't the best practice for isotope. There are functions that exist on the isotope object that are needed, you also need to set the option in the new Isotope(ref, options) function, AND if you need to style the div, it's a little awkward to come back to this component.

    It seems a better practice is to instead place the code within this component, into any component you are using isotope in. This way you a) have easy access to the isotope object, b) you can more easily style the container Div, and c) you can more easily pass and edit the isotope options.

    You can of course keep the container as it is, though it becomes necessary to lift the state up, making this component a little unnecessary.

    0 讨论(0)
  • 2020-12-30 03:53

    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.

    0 讨论(0)
  • 2020-12-30 03:55

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

    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.

    0 讨论(0)
  • 2020-12-30 03:58

    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>
        </>
      );
    };
    
    0 讨论(0)
  • 2020-12-30 04:00

    I don't know how but this doesn't work for me. But if I don't use the map function and use data manually this works.

    import React, {useEffect, useState, useRef} from 'react';
    import options from "./Options"
    import ReactDom from 'react-dom'
    import Isotope from 'isotope-layout';
    import ItemGrid from "./ItemGrid";
    
    
    const Home = () => {
        const [question, setQuestion] = useState();
        const [options, setOptions] = useState([]);
        
        // store the isotope object in one state
        const [isotope, setIsotope] = React.useState(null);
    useEffect(() => {
            Axios.get("http://localhost:8080/laravel/voting/public/api/question/3").then((res)=>{
                console.log(res.data)
                setQuestion(res.data.question);
                setOptions(res.data.option)
            });
        }, []);
    
        useEffect(() => {
            setIsotope(
                new Isotope(".filter-container", {
                    itemSelector: ".filter-item",
                    layoutMode: "vertical",
                    getSortData : {number: '.number parseInt'}
                })
            );
        }, []);
    
    
    
        const changeStateLevel = ()=>{
            isotope.arrange({ sortBy: "number" });
        }
        return (
    
            <>
                <div className="row">
                    <div className="col-sm-7">
                        <div className="col-sm-14 mb-sm-5" >
                            <hr />
                            <ul className="filter-container">
                                {
                                    options.map(object=>(
                                            <div className="filter-item vege">
                                                <p className="number">{object.vote}</p>
                                                <span>Cucumber</span>
                                            </div>
                                    ))
                                }
                            </ul>
    
                        </div>
    
                    </div>
                    <div className="col-sm-5">
                    </div>
                    <button className="btn btn-primary" onClick={changeStateLevel}> Change</button>
                </div>
            </>
        );
    }
    
    export default Home;
    
    0 讨论(0)
提交回复
热议问题