Passing value from p5 sketch to React (with react-p5-wrapper) - javascript

I have a p5 webcam video prediction system working right now. Currently, I'm trying to plug this into a React app to create a fuller web app.
My problem is, the prediction is now only made in my p5 sketch, I want the prediction value to be passed into React's App.js for further constructions. Is there any method of doing so?
I'm using react-p5-wrapper btw.
Here's the sketch.js:
import "react-p5-wrapper/node_modules/p5/lib/addons/p5.dom";
import ml5 from 'ml5';
let mobileNet;
let video;
let label='model loading...';
function sketch (p) {
p.setup = function () {
p.createCanvas(1000, 1000);
//imitialize the webcam stream in a object
video = p.createCapture(p.VIDEO);
//hide the webcam stream
video.hide();
//initialize the mobilenet object with a callback
mobileNet= ml5.imageClassifier('MobileNet',video,ModelLoaded);
};
p.draw = function () {
p.image(video,0,0);
p.textSize(16);
p.fill(255,140,0);
p.text(label,10,450);
};
};
function ModelLoaded()
{
console.log('Model is ready');
//predicting the image
mobileNet.predict(result)
}
//callback function to get the results
function result(err,res)
{
//check for errors
if(err)
{
//log the error if any
console.error(err)
}
else{
//get the label from the json result
label = res[0].className;
//predicting the image again
mobileNet.predict(result)
}
}
export default sketch;
And my App.js currently looks like this:
import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
import sketch from './sketch';
import P5Wrapper from 'react-p5-wrapper';
class App extends Component {
componentDidMount(){
}
render() {
return (
<div className="App">
<P5Wrapper sketch={sketch} />
</div>
);
}
}
export default App;
Any help appreciated!

I gave this a go and came up with a solution. It isn't very elegant but it should do. I made a very simple test project in sketch.js where I try to illustrate two ways accessing the infromation. The things to note are the timesClicked variable and the updateWithProps function.
export let timesClicked = 0;
export default function sketch (p) {
p.setup = function () {
p.createCanvas(300, 300);
};
p.draw = function () {
p.background(0);
p.fill(255);
p.ellipse(p.mouseX, p.mouseY, 100, 100);
};
p.updateWithProps() = function(newProps){
if(newProps.getCoords){
p.sendCoords = newProps.getCoords;
}
}
p.mouseClicked = function() {
p.sendCoords(p.mouseX, p.mouseY);
timesClicked++;
}
};
timesClicked is a variable that can be imported and counts the times the mouse has been clicked. It can be modified from inside the sketch scope and imported from other files.
updateWithProps is a function called from the react-p5-wrapper library whenever the component receives props and can be defined inside the sketch.
With this, your App.js file could be modified like so:
import React, { Component } from 'react';
import P5Wrapper from 'react-p5-wrapper';
import sketch from './sketch';
import {timesClicked} from './sketch';
function getCoords(){
console.log(arguments);
}
class App extends Component {
componentDidMount(){
}
render() {
return (
<div className="App">
<P5Wrapper sketch={sketch} getCoords={getCoords}/>
</div>
);
}
}
export default App;
document.body.onkeyup = function(e){
if(e.keyCode == 32){
console.log(timesClicked);
}
}
When running, every time there is a click, the sketch will execute the getCoords() function in the App.js file and, alternatively, every time the space bar is pressed the timesClicked variable will be accessed from the App.js file. I think you can modify this in order to "send" or "read" the prediction value.
Edited as per Daniel's answer

Update from Julian's previous answer:
As of now (August 2022), p.myCustomRedrawAccordingToNewPropsHandler() has been renamed to p.updateWithProps()
Read more: https://github.com/P5-wrapper/react#props

Related

Get current route name in App.js (no access to navigation prop)

in my react native main file (App.js) I have a handler for firebase notifications, in the foregroundNotifications listener I want to check current route name but problem is I don't have access to navigation prop, that's why I created a ref to navigation in another file which I then import in App.js.
In my RootNavigation.js
import * as React from 'react';
export const isReadyRef = React.createRef();
export const navigationRef = React.createRef();
export function navigate(name, params) {
if (isReadyRef.current && navigationRef.current) {
// Perform navigation if the app has mounted
navigationRef.current.navigate(name, params);
} else {
// You can decide what to do if the app hasn't mounted
// You can ignore this, or add these actions to a queue you can call later
}
}
This allowed me to use navigate (added the method following another stackoverflow post) , but how can I get current route name from NavigationRoot.js?
How I use it in my App.js (I added navigate, but how can I add getCurrentRoute?)
import { navigationRef, isReadyRef } from '#env/RootNavigation.js';
RootNavigation.navigate('NewScreen');
<NavigationContainer ref={navigationRef} onReady={ () => { isReadyRef.current = true; } }>
<ROOTSTACK1></ROOTSTACK1>
</NavigationContainer>
Your RootNavigation file should be like in this Example.
add this below function in RootNavigation file
function getCurrentRoute(){
if (navigationRef.isReady()) {
const route=navigationRef.getCurrentRoute();
console.log(route);
// sample output {key:"Home-k2PN5aWMZSKq-6TnLUQNE",name:"Home"}
return route.name;
}
}
And wherever you want to use this function
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
// ...
RootNavigation.getCurrentRoute();

onClick event listeners are not working in Remix

In my Remix app, I'm trying to conditionally display a UI widget, based on the value of a state variable. Here is my code.
import { useState } from "react";
import type { LinksFunction } from "remix";
import stylesUrl from "../styles/index.css";
export const links: LinksFunction = () => {
return [
{
rel: "stylesheet",
href: stylesUrl
}
];
};
export default function Index() {
const [isMenuOpen,setMenuOpen] = useState(false)
function toggleNav(){
window.alert("hh") // no alert is shown
console.log("hi") // no console statement is printed
setMenuOpen(!isMenuOpen)
}
return (
<div className="landing">
<button onClick={toggleNav}>test</button>
</div>
);
}
However, toggleNav function doesn't seem to be triggered on button click. I couldn't see any alert or output in the console.
I couldn't understand why it's not working. It would be great, if someone can point me out what I'm doing wrong here. TIA.
Ensure that you are rendering the Scripts component from Remix in the root route, without it you app will not load any JS client side.
See https://remix.run/docs/en/v1/api/remix#meta-links-scripts

React Jest Testing button click called a function

I am very new to React and want to be able to test that a function was called when a button is clicked, however I cannot get this working. I have tried the answers at posts such as: How to test a function is called in react jest? and others but to no avail.
My component snippet is:
// Import React core components
import React from 'react';
class ScrollToTop extends React.Component {
/**
* Method to scroll top the top of the page on click
*/
scrollToTopEvent() {
window.scrollTo(0, 0);
};
render() {
return(
<div id="scroll-to-top-container">
{
this.state.showScrollToTop ? (
<button id="scroll-to-top" className="button button-secondary" onClick={ this.scrollToTopEvent }>Scroll to top</button>
) : (
null
)
}
</div>
);
}
export default ScrollToTop;
}
And here is my test that I am attempting to get working, cobbled together from various Stackoverflows and articles:
import React from 'react';
import { shallow } from 'enzyme';
import ScrollToTop from "./scroll-to-top";
describe(`Tests the scroll-to-top.js file content`, () => {
const scrollToTop = shallow(<ScrollToTop />);
const instance = scrollToTop.instance();
describe(`Tests scrollToTopEvent`, () => {
it(`should ensure the scrollToTop method was called on button click`, () => {
scrollToTop.setState({ showScrollToTop: true });
jest.spyOn(instance, 'scrollToTopEvent');
const scrollToTopButton = scrollToTop.find(`#scroll-to-top`);
scrollToTopButton.simulate('click');
scrollToTop.update();
expect(instance.scrollToTopEvent).toHaveBeenCalled();
});
});
});
The error I get in the console is:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
> 96 | expect(instance.scrollToTopEvent).toHaveBeenCalled();
Any help for this n00b would be much appreciated!
To be enable enzyme to track a function call you have to spy on that function, like this:
const scrollToTopEventSpy = jest.spyOn(ScrollToTop.prototype, 'scrollToTopEvent);
Then you can test the calls like this:
expect(scrollToTopEventSpy).toHaveBeenCalled();

How to implement a class controller option in Flare React package

Update: I added a codesanbox at the bottom of this with a more detailed explanation of what I need done.
So I don't quite understand how classes work in React, I am new to react and have used useState a little bit, but never have know what to do with a class. I am using this react package that has an example of how to use a controller to make you animation interactive built with flare now called rive. https://github.com/2d-inc/Flare-React#readme
What I want to achieve is to either run a different animation, or the same animation again when I hover over canvas element that is generated. I can create a second animation in flate(rive) that would still output in the same .flr file and I should then be able to reference it in the controller and run it on hover, just stuck on how to even do that part, or even get this controller to work. One thing to note is I can get the animation to run fine without the controller.
In the docs they have this example code
class PenguinController extends FlareComponent.Controller
{
constructor()
{
super();
this._MusicWalk = null;
this._Walk = null;
this._WalkTime = 0;
}
initialize(artboard)
{
this._MusicWalk = artboard.getAnimation("music_walk");
this._Walk = artboard.getAnimation("walk");
}
advance(artboard, elapsed)
{
// advance the walk time
this._WalkTime += elapsed;
const { _MusicWalk: musicWalk, _Walk: walk, _WalkTime: walkTime } = this;
// mix the two animations together by applying one and then the other (note that order matters).
walk.apply(walkTime % walk.duration, artboard, 1.0);
// if you want to slowly disable the head bobbing (musicWalk animation) you could ramp down the
// final argument (the mix argument) to 0.0 over some time. For now we're mixing at full strength.
musicWalk.apply(walkTime % musicWalk.duration, artboard, 1.0);
// keep rendering
return true;
}
}
First of all what is a constructor? what does super mean? then what are they defining in the constructor, is that some state, how do I determine what to define here?
For the initialized I assume I match it to the state above, and get the animation by the name I named it in flare(rive)
The advance part I don't really understand are we setting the animation with this._WalkTime += elapsed; to how long the animation runs? I think I understand the apply section, it is applying a duration to the animation.
Next it has this code
class MyComponent extends React.Component
{
constructor()
{
this.state = { penguinController: new PenguinController() };
}
render()
{
return <FlareComponent controller={this.state.penguinController} /*... more properties here ...*/ />;
}
}
Here is my attempted code currently I get the following error
TypeError: Cannot set property 'state' of undefined
import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import FlareComponent from 'flare-react';
import styled from 'styled-components'
import Header from "./header"
import "../sass/index.scss"
const LogoWrapper = styled.div`
width:200px;
height:200px;
`
class AnimationController extends FlareComponent.Controller
{
constructor()
{
super();
this._MusicWalk = null;
}
initialize(artboard)
{
this._MusicWalk = artboard.getAnimation("Wispy Appear");
}
advance(artboard, elapsed)
{
const { _MusicWalk: musicWalk } = this;
musicWalk.apply(musicWalk.duration, artboard, 1.0);
// keep rendering
return true;
}
}
const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`)
this.state = { AnimationController: new AnimationController() };
return (
<>
<LogoWrapper>
<FlareComponent width={200} height={200} animationName="Wispy Appear" controller={this.state.AnimationController} file="/wispy-2.flr"/>
</LogoWrapper>
<main className="main-wrapper">{children}</main>
<footer>
© {new Date().getFullYear()}, Built with
{` `}
Gatsby
</footer>
</>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
For reference there is a a tutorial on how to make an animation interactive but it's for flutter, but it has some insights into there api https://medium.com/rive/building-a-responsive-house-with-flare-flutter-31af823ba805
Update
Here is my new attempted code after trying to read up on classes in es6, I still need to learn more.
So how do I go about running a second animation on click or hover or any event. The animation runs once now, but I don't know whow to use the controller?
import React from "react"
import Img from 'gatsby-image'
import styled from "styled-components"
import FlareComponent from 'flare-react';
import Layout from "../components/layout"
const LogoWrapper = styled.div`
width:200px;
height:200px;
`
class wispyController extends FlareComponent.Controller
{
constructor()
{
super();
this._Animate = null;
}
initialize(artboard)
{
this._Animate = artboard.getAnimation("Wispy Appear");
}
advance(artboard, elapsed)
{
const { _Animate: animate } = this;
animate.apply(5, artboard, 1.0);
// keep rendering
return true;
}
}
class IndexPage extends React.Component {
constructor()
{
super();
this.state = { wispyController: new wispyController() };
}
render(){
const {data} = this.props;
return(
<Layout>
<LogoWrapper>
<FlareComponent width={200} height={200} animationName="Wispy Appear" controller={this.state.wispyController} file="/Wispy.flr"/>
</LogoWrapper>
{data.allSanityBlogPost.edges.map(({ node: post }) => (
<article key={post.id}>
<h1>{post.name}</h1>
<img src={post.imageGif.asset.url}/>
</article>
))}
</Layout>
)
}
}
export default IndexPage
export const query = graphql`
query IndexQuery {
allSanityBlogPost {
edges {
node {
name
id
imageGif {
asset {
url
}
}
}
}
}
}
`
Ok Hopefully someone can help here, I made a codesandbox so someone can see what I am trying to achieve. There are two animations, on the page you can see the first one which has a controller which should be mixing the two, and then the other two animations on there own. What I want to happen is the first animation run and then the second one to run on hover. HEre is the code sandbox https://codesandbox.io/s/frosty-water-jnj9m
Classes in the context of React can be thought of as classes in most Object Oriented Programming language, they are a blueprint to create an object from. In order to let the language know how and what to do when we create it, it needs a constructor method. This constructor method is calling a special method called super() so that it calls the constructor of the class it is extending from, in this case FlareComponent.Controller
The method advance will be called to add to the captured walk time that the class is keepi track of.
One of the problems is that you are trying to set the state of the component directly instead of using setState https://reactjs.org/docs/react-component.html
I would highly recommend brushing up on React basics before you continue with this, it will really help you get the proper foundation you need.

ReactJS how to call a component function from another function on the same file?

How can i call a component function from function declared on the outside? Here is the code
function testFunction(){
test();
}
class ChatManager extends Component{
test(){
console.log("Test");
}
render(){
return(
<div className="divChatManager" onClick={()=>testFunction()}>
</div>
)
}
}
How can i call a component function from function declared on the outside? Here is the code
function testFunction(){
test();
}
class ChatManager extends Component{
test(){
console.log("Test");
}
render(){
return(
<div className="divChatManager" onClick={()=>testFunction()}>
</div>
)
}
}
EDITED2
Here is what i am trying to achieve, but couldn't get it working because pushToDetail was inside the ChatManager.js
Error: Attempted import error: 'pushToDetail' is not exported from './components/ChatManager'.
Api.js
import openSocket from 'socket.io-client';
import axios from 'axios';
import { store } from './components/Store'
import { pushToDetail } from './components/ChatManager'
const socket = openSocket("http://localhost:3000/",
{'forceNew': true,
'reconnection': true,
'reconnectionDelay': 1000,
'reconnectionDelayMax': 5000,
'reconnectionAttempts': 999999,
});
function connectToChatRoom(receiver_id){
socket.on(receiver_id, (from_id, message)=>{
console.log(receiver_id + " has received " + message + " from " + from_id);
pushToDetail(message) // I want to send the message from here to ChatManager.js
});
}
ChatManager.js
import ChatManagerStyle from '../styles/ChatManagerStyle.scss';
import axios from 'axios';
import { store } from './Store'
import ChatItem from './ChatItem';
import ChatDetailItem from './ChatDetailItem';
import { connectToChatRoom, disconnectFromChatRoom, socket } from '../Api';
class ChatManager extends Component{
state = {
chatItem: [],
chatDetail: [],
}
constructor(props){
super(props);
};
pushToDetail = (message) => {
this.setState({ chatDetail:[...this.state.chatDetail, message] });
}
}
export { ChatManager, pushToDetail };
There are at least two ways that I can think of to reach your expected behavior, neither of which I recommend. First one by adding test as a static method to the class itself:
function testFunction(){
ChatManager.test(); // Chatmanager itself is an object
}
class ChatManager extends Component{
static test(){ // set test as static function
console.log("Test");
}
}
Second method by using the bind() method to attribute test() function to an instance:
function testFunction(){
this.test(); // call test function on an instance
}
class ChatManager extends Component{
test(){
console.log("Test");
}
render(){
return(
<div className="divChatManager" onClick={ testFunction.bind(this) }>
</div>
)
}
}
Both solutions are very hacky, and as others have mentioned before, the major question you should be asking yourself is why do you want to do this in the first place? Chances are, there is an issue that can be fixed within the conventions of React and not using hacky javascript.
This seems like you're trying to combine 2 concepts. A Component is your view logic. If you need to call a component's method, mark it static but you shouldn't really be mixing view logic with external business logic. That's what utility folders/files are for or even a reducer/middleware pair.
I think what you really want is to define a helper function or factory in a utility file that takes whatever arguments you need to take to produce your output.
If I'm not wrong, eventually your are trying to setState with message. trigger an action to update state using reducer function.

Categories