We learned in the previous section that you can use Koji.playerState.context
to determine the initial context, which will be default
or remix
.
But when a user is creating a remix, they can freely move back and forth between the remix and preview modes.
When they preview the changes, it would be helpful to show them the same content that is shown in the default context, so that the user can see something similar to what they’ll see after they publish the remix.
To enable this preview, you can use Koji.remix.subscribe
, which allows the application to listen to the user’s actions when toggling back and forth between the remix and preview modes.
Update /frontend/src/App.js
to look like the following.
import Koji from '@withkoji/core';
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [logoSrc, setLogoSrc] = useState(Koji.remix.get().spinningImageSrc);
const { context } = Koji.playerState;
const captureImage = async () => {
const src = await Koji.ui.capture.image();
if (src) {
setLogoSrc(() => src);
await Koji.remix.set({ spinningImageSrc: src });
}
};
const [userIsRemixing, setUserIsRemixing] = useState(context === 'remix');
useEffect(() => {
// The `Koji.playerState.subscribe` function returns an unsubscribe function to
// remove the listener.
const unsubscribe = Koji.playerState.subscribe((isRemixing) => {
setUserIsRemixing(() => isRemixing);
});
return () => {
// When this component unmounts, call the unsubscribe function to clean up
// the outstanding event listener.
unsubscribe();
};
}, []);
useEffect(() => {
Koji.ready();
}, []);
if (userIsRemixing) {
return (
<div className="App">
<header className="App-header">
<button onClick={captureImage}>Capture Image</button>
</header>
</div>
);
}
return (
<div className="App">
<header className="App-header">
<img src={logoSrc} className="App-logo" alt="logo" />
<p>Some New Text</p>
</header>
</div>
);
}
export default App;
There are a few changes here, so let’s walk through them.
Notice that you’re importing useEffect
from react
at the top of the file.
useEffect
is a React hook that helps to manage side effects, like fetching data or registering event listeners.
You don’t need to worry too much about the hook syntax.
It’s more important to understand what’s being called inside each function.
In this case, you use the first useEffect
to subscribe to an isRemixing
listener via Koji.playerState.subscribe
.
When the user moves into the preview mode, isRemixing
will be false
, and when the user moves back into the remix mode, isRemixing
will be true
.
You can use that listener to keep track of a local state: userIsRemixing
.
You initialized the state with a value of context === 'remix'
, which we know will be true when the user first opens a remix.
The conditional render logic has also been updated, so that the remix content will be shown only if userIsRemixing
is true
.
You use a second useEffect
to call Koji.ready()
.
This method lets the platform know that you’re ready to start receiving the isRemixing
events that will trigger the Koji.playerState.subscribe
listener.
useEffect
blocks run sequentially, so by placing the Koji.ready()
call in a second useEffect
block, you ensure it will be called after you’ve subscribed to the listener.
The final piece is to add a way for the user to toggle between the remix and preview modes.
For that, you can leverage Koji.remix.finish()
.
Let’s do that by adding an additional button to the remix content.
if (userIsRemixing) {
return (
<div className="App">
<header className="App-header">
<button onClick={captureImage}>Capture Image</button>
<button onClick={finish}>Finish</button>
</header>
</div>
);
}
You also need to add the finish
function, right after the captureImage
definition.
const captureImage = async () => {
const src = await Koji.ui.capture.image();
if (src) {
setLogoSrc(() => src);
await Koji.remix.set({ spinningImageSrc: src });
}
};
const finish = () => {
Koji.remix.finish();
};