【问题标题】:React.js and Isotope.jsReact.js 和 Isotope.js
【发布时间】:2014-09-27 21:43:59
【问题描述】:

我正在查看React.js 并试图弄清楚这个库如何与Isotope.js 一起工作。 React 的文档说它可以很好地与其他库一起使用,但是将它与自行更改 DOM 的库一起使用似乎没有使用 React 的意义。

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

【问题讨论】:

    标签: javascript jquery-isotope reactjs


    【解决方案1】:

    我使用 useRef、useState 和 useEffect 挂钩的解决方案。它还适用于动态生成的过滤器键和项目。诀窍是在组件挂载后初始化 Isotope,并在每次过滤器关键字更改时调用其“排列”方法。

    演示:https://codepen.io/ilovepku/pen/zYYKaYy

    const IsotopeReact = () => {
      // init one ref to store the future isotope object
      const isotope = React.useRef()
      // store the filter keyword in a state
      const [filterKey, setFilterKey] = React.useState('*')
    
      // initialize an Isotope object with configs
      React.useEffect(() => {
        isotope.current = new Isotope('.filter-container', {
          itemSelector: '.filter-item',
          layoutMode: 'fitRows',
        })
        // cleanup
        return () => isotope.current.destroy()
      }, [])
    
      // handling filter key change
      React.useEffect(() => {
        filterKey === '*'
          ? isotope.current.arrange({filter: `*`})
          : isotope.current.arrange({filter: `.${filterKey}`})
      }, [filterKey])
    
      const handleFilterKeyChange = key => () => setFilterKey(key)
    
      return (
        <>
          <ul>
            <li onClick={handleFilterKeyChange('*')}>Show Both</li>
            <li onClick={handleFilterKeyChange('vege')}>Show Veges</li>
            <li onClick={handleFilterKeyChange('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>
        </>
      )
    }
    

    更新(17/11/21):

    TypeScript 演示:https://codesandbox.io/s/react-isotope-typescript-i9x5v

    别忘了添加 @types/isotope-layout 作为开发依赖项。

    【讨论】:

    • 我收到了 2 个错误,例如:1) 'Isotope' 未定义 2)'ReactDOM' 未定义 ...请帮助我为什么会出现这种情况。我做错了什么? :(
    • @TanBirDeveloper 您是否还导入了库react-domisotope-layout
    • 感谢您的回复,是的,我已经导入了 react-dom 和 isotope-layout,所以现在可以正常工作了 :) 非常感谢
    【解决方案2】:

    更新了休伯特对 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
    

    编辑:几个月不使用后回到同位素。使用上述容器组件并不是同位素的最佳实践。同位素对象上存在需要的函数,你还需要在new Isotope(ref, options)函数中设置选项,如果你需要设置div的样式,回到这个组件有点尴尬。

    似乎更好的做法是将代码放在此组件中,放入您正在使用同位素的任何组件中。这样您a)可以轻松访问同位素对象,b)您可以更轻松地设置容器 Div , 和 c) 您可以更轻松地传递和编辑同位素选项。

    你当然可以保持容器原样,尽管有必要提升状态,这使得这个组件有点不必要。

    【讨论】:

      【解决方案3】:

      我不知道怎么做,但这对我不起作用。但是,如果我不使用地图功能并手动使用数据,这是可行的。

      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;
      

      【讨论】:

        【解决方案4】:

        这是 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;
        

        用法:

        只需将要保留在同位素内的项目作为子项传递给 ItemGrid 组件:

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

        替代方案

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

        【讨论】:

          【解决方案5】:

          我按照 Amith 在 this link 的快速教程让 Isotope 在 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】:

            这是一个使用 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();
                    }
                }
            });
            

            【讨论】:

            • 它似乎不起作用,请您重温小提琴。
            • 我们可以重温小提琴吗?
            【解决方案7】:

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

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

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

            这就是它的样子:


            集成 React 和 Isotope 之类的库的问题在于,您最终将有 2 个不同的库尝试更新同一个 dom 子树。由于 React 使用差异,它有点假设它是单独修改 dom。

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

            shouldComponentUpdate: function() { 
                return false; 
            }
            

            有了这个,你可以:

            • 使用 React 生成同位素项 html 元素(您也可以不使用 React 创建它们)
            • componentDidMount,初始化React挂载的dom节点上的同位素

            仅此而已。现在 React 再也不会更新这部分 dom,Isotope 可以随意操作它,而不会干扰 React。

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

            【讨论】:

              【解决方案8】:

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

              使用我的mixin 来解决问题:)

              【讨论】:

                猜你喜欢
                • 2013-04-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-07-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多