React onTouchStart not firing - reactjs

I am trying to implement a swipe feature in ReactJS, but before I even really dive in, I cannot seem to get the onTouchStart event listener to work. I have looked online but most answers are outdated, or do not address my question directly. This link is where I got the most information thus far, but it still falls short of my question and some of the answers are outdated. What's the proper way of binding touchstart on React JS?
I went down to creating the simplest form of the functionality and included that code below. I was trying to console.log when onTouchStart={this.swiped}> occurs. On a side note, if I change the listener to onClick onClick={this.swiped}>, this works immediately.
class App extends React.Component {
contructor() {
super();
this.swiped = this.swiped.bind(this);
}
swiped() {
console.log("Swiped")
}
render() {
return (
<div>
<div
className='swipe-card'
onTouchStart={this.swiped}>Swipe Me
</div>
</div>
)
}
}
Also, I have added the CSS style cursor: pointer to the element. I also tried adding
componentWillMount: function(){
React.initializeTouchEvents(true);
}
But according to the React blogs, React.initializeTouchEvents is no longer required.
This seems so trivial and something that should be really simple to implement. What am I missing? My goal is to implement this without an external library. Here is a link to Codepen where I was trying to implement this.
https://codepen.io/jorgeh84/pen/mMdBZg

This works for me. Maybe the issue is that you're not testing it on a real device?
class App extends React.Component {
contructor() {
super();
}
swiped = () => {
console.log("Swiped")
}
render() {
return (
<div>
<div className='swipe-card' onTouchStart={this.swiped}>Swipe Me</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('App'))

I realized that Daniel was onto something. So I do not need to be testing on an actual device, however, when using Chrome, you will need to use their device toolbar to simulate a mobile device for this to work.

Related

React: is this an acceptable way to add event listeners to a functional component?

i'm working on my first app using react and i want to attach a event listener to a component. I know theres a lot of ways to do this but i came up with this one:
function loginHandler () {
alert('Hey, you clicked me!');
}
const Header = () => {
return (
<header className="header">
<nav className="home-navbar">
<a className="home-navbar__link home-navbar__logo" href="/">Notepp</a>
<a className="home-navbar__link" href="/">About</a>
<a onClick={loginHandler} className="home-navbar__link home-navbar__login" href="/">Sign in with Google</a>
</nav>
</header>
)
}
I don't want to transform this into a class component and create loginHandler method because this is just a simple nav component and i feel like it would somehow complicate thinks a bit. I'm also not sure on how to use hooks although i heard they are very useful. Of course i'm would separate my loginHandler function into her own file but i just want to hear from more experienced developers first. Now this being said i have actually 2 questions.
Is this okay to do?
On the lessons i took from React, the teachers always used class components and onClick to pass event listeners to components, and i decided to create my own app before learning hooks so...
Should i use onClick to handle events?
People always say its best to separate markup from interactivity and i agree with them, and seeing as onClick is at its core (i think) an HTML event handler, i'm a bit suspicious of them (don't wanna hurt my imaginary good practice points right?). Thanks in advance for your help.
Of course, your code is fine, React itself encourages using functional components (with the addition of hooks, memo, ...).
This is almost an exact copy of the code in the official React documentation for Handling Events:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
I'd suggest at least going through "Main concepts" of the official documentation, before developing your first application. It also has a section on Function and Class Components.
The only remark I could make about this code is that if the onClick will take you to a different screen, then you might look at something like react-router so the URL changes as well.

How to refresh the facebook like url in reactjs (using facebook-sdk CDN)

I am using Facebook's like button as generated by facebook's like button configurator. However in order to get facebook-sdk to finish loading before the Like button, I had to use something called react-load-script and make a my own wrapper component for the like button html I got from the configurator.
my like button
class Like extends React.Component {
state = {
facebookLoaded: false
};
handleFacebookLoaded = () => this.setState({
facebookLoaded: true
});
FacebookSDK = () => <>
<div id="fb-root"></div>
<Script
async defer crossOrigin="anonymous"
url="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.3&appId=391623981325884&autoLogAppEvents=1"
onLoad={this.handleFacebookLoaded}
/>
</>;
render() {
return <>
<this.FacebookSDK />
{this.state.facebookLoaded
? <div class="fb-like" data-href={this.props.url} data-width="" data-layout="button_count" data-action="like" data-size="large" data-show-faces="true" data-share="true" />
: null}
</>;
}
}
In my code all the script loading stuff actually happens in App.jsx, but I moved it into one class just to show a simple version.
This part seems to work fine, the issue lies when changing the url passed to data-href.
I checked the react dom in the browser and the data-href is actually being updated properly, however this does not affect the actual url that is being used by the like button, unless I do a full page refresh. I'm assuming this has to do with how the data-href is being used by facebook-sdk. (edit: after testing I'm not sure anymore)
I've found many questions about this on Stack Overflow, however none of them seem to be based off the CDN version of facebook buttons
From what I understand, the div containing the href needs to be placed out and back into the DOM in order for the facebook-sdk to detect a change, but I don't know how to do this in react without a full page refresh. Also I'm not certain this is even the right solution.
-- Update --
I just noticed something else that seems like useful information. If I navigate to the page with the like button, then it doesn't show up. It will only show up if the page refreshes. I tested it by moving the part that loads the script into the like component (like in the example shown above) and that didn't change the behavior at all.
-- more experimenting --
I wrote an event handler that takes all the facebook related jsx out of the dom and back in (by toggling a button) However when all the code goes back into the dom (both jsx and html), the UI for the button does not come back. I'm really now sure how this is possible as I'm literally reloading the script and everything facebook related so this should be equivalent to a page refresh no?
I fixed the issue thanks to misorude. The part I was missing was calling window.FB.XFBML.parse(). I didn't realize I could access FB the same way using the CDN. If anyone is looking for a react solution here is the working code:
class Like extends React.Component {
constructor(props) {
super(props);
this.state = {
url: props.url,
}
}
handleChangePage() {
let likeBtn = document.createElement('div');
likeBtn.className = "fb-like";
likeBtn.setAttribute("data-href", this.props.url);
likeBtn.setAttribute("data-width", "");
likeBtn.setAttribute("data-layout", "button_count");
likeBtn.setAttribute("data-action", "like");
likeBtn.setAttribute("data-size", "large");
likeBtn.setAttribute("data-show-faces", "true");
likeBtn.setAttribute("data-share", "true");
let likePanel = document.getElementById("like-panel");
likePanel.removeChild(likePanel.childNodes[0]);
likePanel.appendChild(likeBtn);
window.FB.XFBML.parse(likePanel)
this.setState({ url: this.props.url });
}
componentDidMount() {
this.handleChangePage();
}
render() {
if(this.props.url !== this.state.url)
this.handleChangePage();
return <div id="like-panel">
{this.props.facebookLoaded
? <div className="fb-like" data-href={this.props.url} data-width="" data-layout="button_count" data-action="like" data-size="large" data-show-faces="true" data-share="true" />
: null}
</div>;
}
}
I moved the CDN out of this component so that it only loads the sdk once for the whole app.

Converting HTMLElement to a React Element, and keeping any event listeners

Rendering some JSON data into a set of collapsible HTML elements is EASY using a library like renderjson (npm package) for Vanilla JS
const data = { sample: [1, 2, 3, 4], data: { a: 1, b: 2, c: ["hello", null] } };
const rjson = renderjson(data);
document.getElementById('to-render').append(rjson);
In The react world
However, renderjson returns an HTMLElement, NOT A STRING. Please note that I'm not JUST trying to convert HTML strings into react HTML here. I'm trying to convert the HTMLElement into a real ReactElement.
So, I actually managed to do this, but since I'm parsing the HTMLElement into a string, all the onClick event listeners generated by renderjson on the + and - handles (to open or collapse the json) are LOST in the process....
Does anyone have any idea on how to convert a HTMLElement object into React Element, keeping all the original event handlers?
I guess, the real question here is: how can the renderjson package be "repackaged" to become React friendly?
Here is a codesandbox to make it easier to see what I'm talking about here.
Good question, I keep running into similar issues with non-React JS libraries that return DOM elements. These libraries should be ported to React, but if you don't want to do that, you can use findDomNode() to get a DOM (not React) node, and append your HTMLElement there. Please note that in the official docs, its use is discouraged because "it pierces the component abstraction". Anyways, in your example, you could add a wrapper class:
class JsonTreeWrapper extends Component {
componentDidMount() {
const renderedJson = renderjson(this.props.data);
const container = findDOMNode(this);
container.appendChild(renderedJson);
}
render() {
return <div/>;
}
}
what this does is render a div, and when the component mounts it finds the div and adds the HTMLElement generated by your library there. Simple.
In your App, you can use it like this:
class App extends Component {
render() {
return (
<div>
<h1> Inside the react world </h1>
<JsonTreeWrapper data={data}/>
</div>
);
}
}
This way your listeners will be working just fine.
Codesandbox didn't work for me, but you can check a demo here. I hope this helps!

onClick not being called ReactJS

Before downvoting, I've been through plenty of other solutions on SO around this same issue and can't find an answer that resolves this issue.
I'm having trouble trying to get the onClick attribute to fire off my function, here's a piece from the problematic component:
Constructor:
constructor() {
super();
this.state = {
submissionFormCount: 0
}
this.addToSubmissionFormCount = this.addToSubmissionFormCount.bind(this);
}
render:
<div className="row">
<div className="col s12 m12 l12">
<h5 onClick={this.addToSubmissionFormCount} style={beatSubmissionStyles.colorize}><span>(Plus) </span>add another beat</h5>
</div>
</div>
clickHandler:
addToSubmissionFormCount() {
alert('Here');
this.setState({
submissionFormCount: this.state.submissionFormCount++
});
}
I'm rendering the app from an Express server using 'react-dom/server'
Here's how I am rendering the component:
exports.beatSubmission = (req, res) => {
const appString = renderToString(<App type="beatSubmission"/>);
res.send(beatSubmissionTemplate({
body: appString
}))
}
I think you're only rendering your react components on the server side. The reason I think this is because of the following code you've copied:
exports.beatSubmission = (req, res) => {
const appString = renderToString(<App type="beatSubmission"/>);
res.send(beatSubmissionTemplate({
body: appString
}))
}
You're rendering the component to a string and shipping the string to the frontend as static HTML. While this will indeed give you properly rendered markup, it will result in a non-interactive app.
To have click handlers work, you also need to compile your JS, and include it for use on the frontend like this:
https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/server.js#L76
renderToString(<Html assets={webpackIsomorphicTools.assets()} store={store}/>));
There are a few ways to do this. You can manually add a script file with your JS pre-packaged for the frontend in your template or you can use Webpack Isomorphic Tools.
I suspect you read a react tutorial that lead you down the isomorphic (server/client side rendered) path. You can run react on the server, the client side or both. Running it on both takes some work, but results in an app that "feels" faster.
I can't see any problem with the code. However, the only thing that comes to my mind is if you're rendering the <h5> in a function of some sort, maybe mapping and array for example. If so, you need to define var self = this in the render function before the return, then use self.addToSubmissionFormCount.
P.S. I don't recommend using onClick handler in <h5> tag
Change your h5 into an anchor tag. So replace:
<h5 onClick={this.addToSubmissionFormCount} style={beatSubmissionStyles.colorize}><span>(Plus) </span>add another beat</h5>
with:
<a onClick={this.addToSubmissionFormCount} style={beatSubmissionStyles.colorize}><span>(Plus) </span>add another beat</a>
You can style your anchor anyway you want afterwards. For example if you want to not have a cursor when hovering then add cursor: none
You should not mutate the state of react.
Change:
this.state.submissionFormCount++
To:
this.state.submissionFormCount+1

How do properly use Onsen's Navigator in React?

I'm trying to implement a simple Onsen Navigator in React.
So far I'm receiving an error 'route is not defined' and I was looking through the examples & docs but I only saw the initialRoute prop was provided, how & where does the route prop generated or something? Cause it seems like its not specified.
Here is my the code of my component:
import React, {PropTypes} from 'react';
import ons from 'onsenui';
import * as Ons from 'react-onsenui';
class SignUp extends React.Component {
constructor(props) {
super(props);
this.state = {
index : 0
};
this.renderPage = this.renderPage.bind(this);
this.pushPage = this.pushPage.bind(this);
}
pushPage(navigator) {
navigator.pushPage({
title: `Another page ${this.state.index}`,
hasBackButton: true
});
this.setState({index: this.state.index++});
}
renderPage(route, navigator) {
return (
<Ons.Page key={route.title}>
<section style={{margin: '16px', textAlign: 'center'}}>
<Ons.Button onClick={this.pushPage}>
Push Page
</Ons.Button>
</section>
</Ons.Page>
);
}
render() {
return (
<Ons.Page key={route.title}>
<Ons.Navigator
renderPage={this.renderPage}
initialRoute={{
title: 'First page',
hasBackButton: false
}}
/>
</Ons.Page>
);
}
};
SignUp.propTypes = {
'data-pageName': PropTypes.string.isRequired
};
export default SignUp;
Is this the right syntax in ES6? Have I missed something?
When using Ons.Navigator in react the two required properties are:
initialRoute - it should be an object.
renderPage - method which receives 2 arguments - route and navigator. The route should be an object similar to the initialRoute one. You provide that object when you are calling pushPage and similar methods.
It seems that you already know these 2, but there still 2 other things which you need to be careful about. They are not directly onsen related, but come up a lot when using react in general.
Whenever you have a list of dom elements (for example an array of Ons.Page tags) each of those should have a unique key property.
Whenever you use a method you need to make sure you are binding it if you need some extra arguments.
It seems you also know these two. So the only thing left is to make sure you follow them.
Your syntax is correct - the only thing missing is the route variable in SignUp.render. Maybe you originally copied the renderPage method and that is how you have a leftover Ons.Page.
If you're not putting the SignUp component inside some other navigator, tabbar or splitter then you don't actually need the Ons.Page in its render method. Those are the only cases when they are needed. If you it happens to have one of those components as a parent then you can just specify the key.
PS: I think there should be a React Component Inspector (something like this) which you can install - then I think you may be able to see the place where the error occurs. I think if you knew on which line the problem was you would have been able to solve it. :)
For me, with the object I was passing to initialRoute(), it needed a props property, which itself was an object with a key property. See the before and after below.
Before fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage}}
renderPage={this.renderPage}
/>
);
}
}
This was causing the following console warning:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `Navigator`.
After fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage, props: {key: 'DataEntryPage'}}}
renderPage={this.renderPage}
/>
);
}
}
Notice that the difference I needed to make was the addition of , props: {key: 'DataEntryPage'}.
Feel free to check out this medium article for more information.

Resources