javascript React.js 和 Isotope.js
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25135261/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React.js and Isotope.js
提问by user1091733
I'm checking out React.jsand 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.
我正在查看React.js并试图弄清楚这个库如何与Isotope.js一起工作。React 的文档说它可以很好地与其他库一起使用,但是将它与自行更改 DOM 的库一起使用似乎没有使用 React 的意义。
Can someone explain to me, how can I take advantage of React in my webapp that uses Isotope.js as layout ?
有人可以向我解释一下,如何在使用 Isotope.js 作为布局的 web 应用程序中利用 React?
采纳答案by Sebastien Lorber
You can manipulate the dom directly inside React. This permits to integrate existing JS libraries or for custom needs not handled well by React.
你可以直接在 React 中操作 dom。这允许集成现有的 JS 库或用于 React 无法很好处理的自定义需求。
You can find an exemple here:
你可以在这里找到一个例子:
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.
React 和 Isotope 之类的库集成的问题在于,您最终将有 2 个不同的库试图更新同一个 dom 子树。当 React 使用 diff 时,它有点假设它是单独修改 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:
所以这个想法可能是创建一个只渲染一次并且永远不会更新自己的 React 组件。您可以通过以下方式确保这一点:
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
- 使用 React 生成您的同位素项目 html 元素(您也可以在没有 React 的情况下创建它们)
- on
componentDidMount
,在React挂载的dom节点上初始化isotope
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.
就这样。现在 React 将永远不会再更新 dom 的这一部分,并且 Isotope 可以随意操作它,而不会干扰 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.
此外,据我所知,Isotope 并不打算与动态的项目列表一起使用,因此拥有一个永不更新的 React 组件是有意义的。
回答by James Broad
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/.
这是 Masonry 的工作版本,您应该会发现很容易移植到 Isotope(或使用 Masonry :))http://jsfiddle.net/emy7x0dc/1/。
Here's the crux of the code that makes it work (and allow React to do its job).
这是使其工作(并允许 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();
}
}
});
回答by Hubert
Here's an updated version of the above code posted by James:
这是 James 发布的上述代码的更新版本:
If you're using webpack, remember to modify your webpack configfor working with Isotope.
如果您正在使用 webpack,请记住修改您的 webpack 配置以使用 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 组件中即可:
<ItemGrid>
{data.map(object => (
<Item key={object._id} name={object.name} imageUrl={object.imageUrl} />
))}
</ItemGrid>
Alternatives
备择方案
If you can, consider using react-masonry-component.
如果可以,请考虑使用react-masonry-component。
回答by Sean Lee
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.
我的 useState 和 useEffect 钩子解决方案也适用于动态生成的过滤器键和项目。诀窍是在组件安装后初始化 Isotope,并在每次过滤器关键字更改时调用其“排列”方法。您可以在类组件中使用 componentDidMount 和 componentDidUpdate 实现相同的效果。
Demo: https://codepen.io/ilovepku/pen/zYYKaYy
演示: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>
</>
);
};
回答by Karén
回答by Iskeraet
Updated Hubert's answer to modern react with Hooks.
更新了休伯特对 Hooks 的现代反应的回答。
import React, { useEffect, useRef, useState } from 'react';
import Isotope from 'isotope-layout'
function IsoContainer (props){
const isoRef = useRef()
const [state, setState] = useState({ isotope: null })
useEffect(() => {
if (!state.isotope) {
setState({
isotope: new Isotope( isoRef.current )
})
} else if (state.isotope) {
state.isotope.reloadItems()
}
})
return (
<div ref={isoRef}>
{props.children}
</div>
)
}
export default IsoContainer
回答by Luke V
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:
通过在此链接中遵循 Amith 的快速教程,我让 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>
)
}
}
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.
它现在的工作方式与在我的静态 jQuery 站点上完全一样。如果您希望过滤器按钮在活动时更改外观,您可以简单地更新 onFilterChange 函数中的本地状态并基于此渲染按钮。