I’m trying to implement the Shotstack Studio Editor in my node.js / react project, using the code example found in the shotstack github here: [shotstack-studio-sdk/demos/react at master · shotstack/shotstack-studio-sdk · GitHub](https://Shotstack Github).
Everything is working perfectly, I can load the editor, playback the example templates and have full functionality in my react project running on localhost.
BUT I have an issue where if I change anything in the code in the file where the tag is inserted, the entire Studio Editor is inserted multiple times? Whenever the page changes somehow (even if I just add or change a single line of code, update the state or whatever) instead of just refreshing the component it inserts a new version of the editor? Has anyone else run into this problem?
The errors I get in the console when loading the Editor is a lot of various “Cookie rejected because it’s in a cross-site context” and also a console log saying that “Shotstack off method is not available”.
I don’t have this issue with components appearing multiple times in any other projects, components or pages, so I’m currently ruling out issues with my react setup.
Anyway, any help would be greatly appreciated
Here’s my code for the StudioEditor, basically copy-pasted from the Github.
import { useEffect, useRef } from ‘react’;
import { useShotstack } from ‘./ShotstackContext’;
const StudioEditor = ({owner, interactive, timeline, sidepanel, controls, settings, style, template, onUpdateEvent, onMetadataEvent,}) => {
const shotstack = useShotstack();
const isMounted = useRef(false);
useEffect(() => {
if (!shotstack) return;
const options = {
owner,
controls,
interactive,
timeline,
settings,
sidepanel,
style,
};
if (shotstack.create) {
shotstack.create('studio-sdk-editor', template, options);
} else {
console.error('Shotstack create method is not available');
}
if (shotstack.on) {
shotstack.on('update', onUpdateEvent);
shotstack.on('metadata', onMetadataEvent);
} else {
console.error('Shotstack on method is not available');
}
return () => {
if (shotstack.off) {
shotstack.off('update', onUpdateEvent);
} else {
console.error('Shotstack off method is not available');
}
};
}, [shotstack, interactive, timeline, sidepanel, settings, style, template, onUpdateEvent]);
return
;};
export default StudioEditor;
And also here’s the code for the main react file where the StudioEditor component is used:
//basic
import { useState } from ‘react’;
import Container from ‘@mui/material/Container’;
import Grid from ‘@mui/material/Unstable_Grid2’;
import Button from ‘@mui/material/Button’;
import AddToQueueIcon from ‘@mui/icons-material/AddToQueue’;
import AddIcon from ‘@mui/icons-material/Add’;
import ‘./Texter.css’;
//shotstack
import { useShotstack } from ‘./shotstack_editor/ShotstackContext’;
import StudioEditor from ‘./shotstack_editor/StudioEditor’;
import firstTemplate from ‘./shotstack_editor/template3.json’;
function Texter () {
// eslint-disable-next-line
const shotstack = useShotstack();
const [template, setTemplate] = useState(firstTemplate);
const handleUpdateEvent = event => {
//console.log('Update event received:', event);
};
const handleMetadataEvent = event => {
//console.log('Metadata event received:', event);
};
const addClipToTrack = (trackIndex) => {
var currentEdit = window.shotstack.edit;
const newClip =
{
"asset": {
"type": "video",
"src": "https://shotstack-assets.s3-ap-southeast-2.amazonaws.com/footage/earth.mp4"
},
"start": 0,
"length": "auto"
};
const newEdit = {
...currentEdit,
timeline: {
...currentEdit.timeline,
tracks: currentEdit.timeline.tracks.map((track, index) =>
index === trackIndex
? {
...track,
clips: [...track.clips, newClip], // Add new clip to the specified track
}
: track
),
},
};
// Reload the Studio with the updated Edit JSON object
//window.shotstack.load('studio', newEdit);
setTemplate(newEdit);
};
function addTrackToTimeline () {
console.log(template)
}
function logState () {
console.log(template.timeline.tracks);
}
return (
<Container maxWidth="xl">
<main>
<Grid container spacing={4}>
<Grid xs={12}>
<Button aria-label="upload picture" component="label" onChange={() => addClipToTrack(0)}>
<input hidden accept="video/*, audio/*, image/*" multiple type="file" />
<AddToQueueIcon className="createClipUploadIcon c4darktext" sx={{fontSize: 40}} />
</Button>
<Button aria-label="upload picture" component="label" onClick={() => logState()}>
<AddIcon sx={{fontSize: 40}} />
</Button>
</Grid>
<Grid xs={12}>
<StudioEditor
owner="oknugu1pfd"
interactive={true}
timeline={true}
sidepanel={false}
controls={true}
settings={true}
template={template}
onUpdateEvent={handleUpdateEvent}
onMetadataEvent={handleMetadataEvent}
/>
</Grid>
</Grid>
</main>
</Container>
);
}
export default Texter;