React.js и Isotope.js
Я проверяю React.js и пытаюсь выяснить, как эта библиотека может работать вместе с Isotope.js.
В документации React говорится, что он отлично работает с другими библиотеками, но использование его с библиотекой, которая меняет DOM самостоятельно, похоже, не имеет смысла использовать React.
Может кто-нибудь объяснить мне, как я могу использовать React в моем webapp, который использует Isotope.js как макет?
Ответы
Ответ 1
Вы можете манипулировать dom непосредственно внутри React. Это позволяет интегрировать существующие JS-библиотеки или для пользовательских запросов, которые не обрабатываются React.
Здесь вы можете найти пример:
https://github.com/stample/gulp-browserify-react-phonegap-starter/blob/master/src/js/home/homeComponents.jsx#L22
И вот как это выглядит:
![image]()
Проблема с интеграцией React и библиотеки, такой как Isotope, заключается в том, что у вас будет две разные библиотеки, пытающиеся обновить одно и то же dom поддерева. Поскольку React работает с diffs, он предполагает, что он один modyfing dom.
Таким образом, идея может заключаться в создании компонента React, который будет отображаться только один раз и никогда не будет обновляться. Вы можете обеспечить это с помощью
shouldComponentUpdate: function() {
return false;
}
С помощью этого вы можете:
- Используйте React для генерации ваших элементов html элементов изотопа (вы также можете создавать их без React)
- В
componentDidMount
инициализируйте изотоп на dom node, установленный React
И это все. Теперь React никогда не будет обновлять эту часть dom снова, и Isotope может свободно манипулировать им, как хочет, не вмешиваясь в React.
Кроме того, насколько я понимаю, Isotope не намерен использоваться с динамическим списком элементов, поэтому имеет смысл иметь компонент React, который никогда не обновляется.
Ответ 2
Здесь рабочая версия с масонством, вам должно быть достаточно легко отправить в 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 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
Вот обновленная версия вышеприведенного кода, опубликованного Джеймсом:
Если вы используете веб-пакет, не забудьте изменить конфигурацию веб-пакета для работы с изотопом.
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>
Альтернативы
Если вы можете, рассмотрите возможность использования response-masonry-component.
Ответ 4
Вам нужно создать новый объект Isotope на компонентеDidMount и перезагрузить элементы в компонентеDidUpdate.
Используйте mixin, чтобы понять:
Ответ 5
Я научился работать с Изотопом в 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
Мое решение с хуками useState и useEffect также работает с динамически генерируемыми ключами и элементами фильтра. Хитрость заключается в том, чтобы инициализировать Isotope после монтирования компонента и вызывать его метод "range "при каждом изменении ключевого слова фильтра. Вы можете добиться того же с помощью 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>
</>
);
};