React.js和Isotope.js

时间:2014-08-05 09:21:25

标签: javascript jquery-isotope reactjs

我正在查看React.js并尝试弄清楚此库如何与Isotope.js一起使用。 React的文档说它可以很好地与其他库一起使用,但是将它与自己更改DOM的库一起使用似乎没有使用React的感觉。

有人可以向我解释一下,如何在我使用Isotope.js作为布局的webapp中利用React?

7 个答案:

答案 0 :(得分:11)

这里是Masonry的工作版本,你应该会发现很容易移植到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'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();
        }
    }
});

答案 1 :(得分:6)

您可以直接在React内部操作dom。这允许集成现有的JS库或React无法很好地处理的自定义需求。

你可以在这里找到一个例子:

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

这就是它的样子:

image


集成React和像Isotope这样的库的问题在于,最终会有2个不同的库试图更新相同的dom子树。由于React可以使用差异,因此它假定它是单独使用dom的。

所以这个想法可能是创建一个只渲染一次的React组件,并且永远不会自行更新。您可以通过以下方式确保这一点:

shouldComponentUpdate: function() { 
    return false; 
}

有了这个你可以:

  • 使用React生成同位素项html元素(您也可以在没有React的情况下创建它们)
  • componentDidMount上,初始化由React
  • 安装的dom节点上的同位素

就是这样。现在React永远不会再次更新dom的这一部分,并且Isotope可以自由地操纵它,而不会干扰React。

另外,据我所知,Isotope并不打算与动态项目列表一起使用,因此有一个永不更新的React组件是有意义的。

答案 2 :(得分:4)

以下是James发布的上述代码的更新版本:

如果您使用的是webpack,请记得modify your webpack config使用Isotope。

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>

替代

如果可以,请考虑使用react-masonry-component

答案 3 :(得分:3)

您需要在componentDidMount上创建新的Isotope对象,并在componentDidUpdate上重新加载项目。

使用我的mixin来弄明白:)

答案 4 :(得分:1)

我的带有useState和useEffect挂钩的解决方案,还可以用于动态生成的过滤器键和项。诀窍是在安装组件后初始化同位素,并在每次filter关键字更改时调用其“ arrange”方法。您可以在类组件中使用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>
    </>
  );
};

答案 5 :(得分:0)

按照this link上Amith的快速教程,让同位素在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 :(得分:0)

我不知道怎么办,但这对我不起作用。但是,如果我不使用地图功能,而是手动使用数据,则可以。

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;