This question is about egjs-flicking library but perhaps problem is more general.
Let us consider two examples of components that will differ only by their render()
method. First I provide the whole component.
import React from 'react';
import Flicking, { MoveEvent, FlickingError } from "@egjs/react-flicking";
class Credits extends React.Component {
constructor(props) {
super(props);
this.readtime = 1000;
this.maxElements = 4;
this.actionReady = this.actionReady.bind(this);
this.actionReset = this.actionReset.bind(this);
this.actionMove = this.actionMove.bind(this);
this.processFlicking = this.processFlicking.bind(this);
}
async actionReady(e) {
const status = e.currentTarget.getStatus();
setTimeout(() => {
e.currentTarget.moveTo(status.position.panel + 1);
}, this.readTime);
}
async actionReset(e, delay) {
setTimeout(() => {
e.currentTarget.moveTo(0);
}, delay);
}
async actionMove(e) {
const status = e.currentTarget.getStatus();
setTimeout(() => {
e.currentTarget.moveTo(status.position.panel + 1);
}, this.readTime);
}
async processFlicking(e) {
const status = e.currentTarget.getStatus();
const remaining = status.panels.length - status.position.panel;
if (remaining == this.maxElements) {
this.actionReset(e, this.readtime*this.maxElements);
} else {
this.actionMove(e);
}
}
getMStyle() {
return {
'overflow': 'hidden'
};
}
render() {
// this is where the examples differ
}
}
Now the first case is the children of the Flicking
container are pre-defined.
render() {
const PanelComponent = this.props.panelComponent;
return (
<Flicking
style={this.getMStyle()}
circular={false}
align={"prev"}
horizontal={false}
onReady={this.actionReady}
onMoveEnd={this.processFlicking}
>
<div>hello</div><div>sup</div><div>how are u</div><div>cze</div><div>konnichiwa</div><div>salam</div><div>namaste</div>
</Flicking>
);
}
which gives the following rendered HTML
<div class="flicking-camera">
<div>hello</div>
<div>sup</div>
<div>how are u</div>
<div>cze</div>
<div>konnichiwa</div>
<div>salam</div>
<div>namaste</div>
</div>
Here they are generated dynamically
render() {
const PanelComponent = this.props.panelComponent;
return (
<Flicking
style={this.getMStyle()}
circular={false}
align={"prev"}
horizontal={false}
onReady={this.actionReady}
onMoveEnd={this.processFlicking}
>
{ this.props.panels.map((data, index) =>
( <PanelComponent key={index} data={data.content} /> )) }
</Flicking>
);
}
giving the following rendered HTML (identical to previous)
<div class="flicking-camera">
<div>hello</div>
<div>sup</div>
<div>how are u</div>
<div>cze</div>
<div>konnichiwa</div>
<div>salam</div>
<div>namaste</div>
</div>
for PanelComponent it was using
class EntryElement extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>{ this.props.data }</div>
)
}
}
to generate each of the panels.
The static case works perfectly, the dynamic case gives the following error
Uncaught (in promise) TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.
at getStyle (utils.ts:259:1)
at Panel.__proto.resize (Panel.ts:323:1)
at Renderer.ts:163:1
at Array.forEach (<anonymous>)
at ReactRenderer.__proto.updatePanelSize (Renderer.ts:163:1)
at Flicking.<anonymous> (Flicking.ts:1227:1)
at step (index.ts:1:1)
at Object.next (index.ts:1:1)
at fulfilled (index.ts:1:1)
and I am really puzzled why because resulting HTML is identical... Any ideas?
Ok I actually found it. All thanks to this GitHub discussion and here is my relevant comment.
From https://naver.github.io/egjs-flicking/docs/quick-start I checked section Bypassing ref forwarding
and added useFindDOMNode={true}
to my Flicking.
Here is the complete working source that is able to dynamically put children components in Flicking
class Credits extends React.Component {
constructor(props) {
super(props);
this.readtime = 1000;
this.maxElements = 4;
this.actionReady = this.actionReady.bind(this);
this.actionReset = this.actionReset.bind(this);
this.actionMove = this.actionMove.bind(this);
this.processFlicking = this.processFlicking.bind(this);
this.createPanel = this.createPanel.bind(this);
}
async actionReady(e) {
const status = e.currentTarget.getStatus();
setTimeout(() => {
e.currentTarget.moveTo(status.position.panel + 1);
}, this.readTime);
}
async actionReset(e, delay) {
setTimeout(() => {
e.currentTarget.moveTo(0);
}, delay);
}
async actionMove(e) {
const status = e.currentTarget.getStatus();
setTimeout(() => {
e.currentTarget.moveTo(status.position.panel + 1);
}, this.readTime);
}
async processFlicking(e) {
const status = e.currentTarget.getStatus();
const remaining = status.panels.length - status.position.panel;
if (remaining == this.maxElements) {
this.actionReset(e, this.readtime*this.maxElements);
} else {
this.actionMove(e);
}
}
getMStyle() {
return {
'overflow': 'hidden'
};
}
createPanel(panel, index) {
const PanelComponent = this.props.panelComponent;
return <PanelComponent data={panel.content} key={index} />;
}
createPanels(panels) {
return panels.map(this.createPanel);
}
render() {
const panels = this.props.panels;
return (
<Flicking
useFindDOMNode={true}
style={this.getMStyle()}
circular={false}
align={"prev"}
horizontal={false}
onReady={this.actionReady}
onMoveEnd={this.processFlicking}
>
{ this.createPanels(panels) }
</Flicking>
);
}
}