How to add my own js script to react? - javascript

I have scirpt for mobile multilevel menu. How can I get it work in react? I have something like this:
export function mobileNavigation() {
let navElements = document.querySelectorAll('.nav');
let backButton = document.querySelector('.back-nav');
navElements.forEach(el => {
el.addEventListener('click', itemClicked)
})
//more code
}
Then in my mobilenav component i have:
componentDidMount() {
mobileNavigation();
}
But it doesnt work.. I mean I know why but I don't know how to solve it. How can I get my script available the whole time not only once when component mounts?

import {mobileNavigation} from '../../some address'
mobileNavigation()

Related

How to bind a react hook to an element created by jquery function

I'm working with a project written in jquery and I want to integrate this code in my react project. I'm trying to send a query to my graphql server by clicking a button.
There is a JQ function that creates multiple elements like this:
canvas.append($(`<div class="d-flex flex-wrap my-2"><img class="else_picture" src="${elem.artworkUrl600}" height="150px"><div class="mx-2"><div class="elseTitle" id="${elem.trackId}">${elem.trackName}</div><h6>by:${elem.artistName}</h6><h6>keywords:</h6><p>${elem.genres}</p><div class="d-flex justify-content-start"><button value="${elem.feedUrl}" class="get_description"><a target="_blank" href="${elem.trackViewUrl}">Info link</a></i></button><div class="like_box"><i id="like" class="fa fa-thumbs-up"></div></div></div><hr>`)
);
My goal is to connect a function "onClick()" to all buttons inside these created elements. For that I'm trying to define a function that would query all elements by id and connect it to a function with a hook to my graphql server:
function connectFunctionalityToLike(){
$("#like").on("click", function(){ Like(this); });
}
function Like(x){
console.log("clicked");
const { loading, error, data } = useQuery(ME_QUERY);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
console.log(data);
}
My function Like() does not really work because I'm missing in understanding how elements in react actually work all together with jquery. I cannot rewrite code in JQuery. I just want to integrate this existing code. Is there any way around connecting react hooks to created elements by id?
Is there
You should create a React component wrapper for the JQuery function.
You will have to manage the React lifecycle manually - by calling the JQuery function in a useEffect() callback and taking care to remove the DOM elements in the cleanup:
function ReactWrapper() {
const { loading, error, data } = useQuery(ME_QUERY);
const [ data, setData ] = React.useState();
React.useEffect(() => {
jqueryFn();
$('#like').on('click', function(){ setData('from click') });
return () => {
/* code that removes the DOM elements created by jqueryFn() */
};
}, []);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return <div>{data}</div>;
}
Then, in this React component, will be allowed to use hooks.
Also, if your JQuery function is so simple, you are probably much better off with rewriting it in React.
As Andy says in his comment: "You shouldn't be mixing jQuery and React." - But, we've all be in spots where we are given no choice. If you have no choice but to use jQuery along side React, the code in the following example may be helpful.
Example is slightly different (no canvas, etc), but you should be able to see what you need.
This example goes beyond your question and anticipates the next - "How can I access and use the data from that [card, div, section, etc] that was added by jQuery?"
Here is a working StackBlitz EDIT - fixed link
Please see the comments in code:
// jQuery Addition - happens outside the React App
$('body').append(
$(`<div class="d-flex flex-wrap my-2"><img class="else_picture" src="${elem.artworkUrl600}" height="10px"><div class="mx-2"><div class="elseTitle" id="${elem.trackId}">${elem.trackName}</div><h6>by:${elem.artistName}</h6><h6>keywords:</h6><p>${elem.genres}</p><div class="d-flex justify-content-start"><button value="${elem.feedUrl}" class="get_description"><a target="_blank" href="${elem.trackViewUrl}">Info link</a></i></button><div class="like_box"><i id="like" class="fa fa-thumbs-up"></div></div></div><hr>`)
);
export default function App() {
// state to hold the like data
const [likes, setLikes] = useState([]);
// create reference to the jquery element of importance
// This ref will reference a jQuery object in .current
const ref = useRef($('.elseTitle'));
// Like function
function Like(x) {
// ref.current is a jQuery object so use .text() method to retrieve textContent
console.log(ref.current.text(), 'hit Like function');
setLikes([...likes, ' :: ' + ref.current.text()]);
}
// This is where you put
useEffect(() => {
// Add event listener and react
$('#like').on('click', function () {
Like(this);
});
// Remove event listener
return () => $('#like').off('click');
});
return (
<div>
<h1>Likes!</h1>
<p>{likes}</p>
</div>
);
}

Custom Element (Web Component) won't accept keyboard input when inserted by a CKEditor 5 plugin

I'm in the initial stages of developing a plugin that will allow the user to insert placeholder elements into HTML content that will be processed server-side and used to incorporate some simple logic into a generated PDF document. To this end, I'm attempting to insert a custom element that I've defined using the web components API.
class NSLoop extends HTMLElement {
constructor() {
super();
}
get source() {
return this.getAttribute('source');
}
get as() {
return this.getAttribute('as');
}
}
window.customElements.define('ns-loop', NSLoop);
The contents of loopediting.js:
import Plugin from "#ckeditor/ckeditor5-core/src/plugin";
import Widget from "#ckeditor/ckeditor5-widget/src/widget";
import {viewToModelPositionOutsideModelElement} from "#ckeditor/ckeditor5-widget/src/utils";
import LoopCommand from "./loopcommand";
export default class LoopEditing extends Plugin {
static get requires() {
return [Widget];
}
constructor(editor) {
super(editor);
}
init() {
this._defineSchema();
this._defineConverters();
this.editor.commands.add('loop', new LoopCommand(this.editor));
this.editor.editing.mapper.on('viewToModelPosition', viewToModelPositionOutsideModelElement(this.editor.model, viewElement => viewElement.is('element', 'ns-loop')));
}
_defineSchema() {
const schema = this.editor.model.schema;
schema.register('loop', {
isBlock: false,
isLimit: false,
isObject: false,
isInline: false,
isSelectable: false,
isContent: false,
allowWhere: '$block',
allowAttributes: ['for', 'as'],
});
schema.extend( '$text', {
allowIn: 'loop'
} );
schema.extend( '$block', {
allowIn: 'loop'
} );
}
_defineConverters() {
const conversion = this.editor.conversion;
conversion.for('upcast').elementToElement({
view: {
name: 'ns-loop',
},
model: (viewElement, {write: modelWriter}) => {
const source = viewElement.getAttribute('for');
const as = viewElement.getAttribute('as');
return modelWriter.createElement('loop', {source: source, as: as});
}
});
conversion.for('editingDowncast').elementToElement({
model: 'loop',
view: (modelItem, {writer: viewWriter}) => {
const widgetElement = createLoopView(modelItem, viewWriter);
return widgetElement;
}
});
function createLoopView(modelItem, viewWriter) {
const source = modelItem.getAttribute('source');
const as = modelItem.getAttribute('as');
const loopElement = viewWriter.createContainerElement('ns-loop', {'for': source, 'as': as});
return loopElement;
}
}
}
This code works, in the sense that an <ns-loop> element is successfully inserted into the editor content; however, I am not able to edit this element's content. Any keyboard input is inserted into a <p> before the <ns-loop> element, and any text selection disappears once the mouse stops moving. Additionally, it is only possible to place the cursor at the beginning of the element.
If I simply swap out 'ns-loop' as the tag name for 'div' or 'p', I am able to type within the element without issue, so I suspect that I am missing something in the schema definition to make CKEditor aware that this element is "allowed" to be typed in, however I have no idea what I may have missed -- as far as I'm aware, that's what I should be achieving with the schema.extend() calls.
I have tried innumerable variations of allowedIn, allowedWhere, inheritAllFrom, isBlock, isLimit, etc within the schema definition, with no apparent change in behaviour.
Can anyone provide any insight?
Edit: Some additional information I just noticed - when the cursor is within the <ns-loop> element, the Heading/Paragraph dropdown menu is empty. That may be relevant.
Edit 2: Aaand I found the culprit staring me in the face.
this.editor.editing.mapper.on('viewToModelPosition', viewToModelPositionOutsideModelElement(this.editor.model, viewElement => viewElement.is('element', 'ns-loop')));
I'm new to the CKE5 plugin space, and was using other plugins as a reference point, and I guess I copied that code from another plugin. Removing that code solves the problem.
As noted in the second edit, the culprit was the code,
this.editor.editing.mapper.on('viewToModelPosition', viewToModelPositionOutsideModelElement(this.editor.model, viewElement => viewElement.is('element', 'ns-loop')));
which I apparently copied from another plugin I was using for reference. Removing this code has solved the immediate problem.
I'll accept this answer and close the question once the 2-day timer is up.

How to add text on border with css for input field using React JS?

I want to create an input box like below,
using css, however am not able to find anything on how to do the text on border for input fields anywhere at all
I tried using but am unable to create the input box as shown above.
I am very much stuck here, it would be a great help if anyone could suggest anyway to create an input field like shown above.
Thank you in advance.
You can use react-native-material-textfield library and need some customization. There is a prop renderLeftAccessory which can render your left view like country code here. Try example code here
import React, { Component } from 'react';
import {
TextField,
FilledTextField,
OutlinedTextField,
} from 'react-native-material-textfield';
class Example extends Component {
fieldRef = React.createRef();
onSubmit = () => {
let { current: field } = this.fieldRef;
console.log(field.value());
};
formatText = (text) => {
return text.replace(/[^+\d]/g, '');
};
render() {
return (
<OutlinedTextField
label='Phone number'
keyboardType='phone-pad'
formatText={this.formatText}
onSubmitEditing={this.onSubmit}
ref={this.fieldRef}
/>
);
}
}

Vue and Prismic rich text: add event listener to a span node

The content of my Vue app is fetched from Prismic (an API CMS). I have a rich text block, some parts of which are wrapped inside span tags with a specific class. I want to get those span nodes with Vue and add to them an event listener.
With JS, this code would work:
var selectedSpanElements = document.querySelectorAll('.className');
selectedSpanElements[0].style.color = "red"
But when I use this code in Vue, I can see that it works just a fraction of a second before Vue updates the DOM. I've tried using this code on mounted, beforeupdate, updated, ready hooks... Nothing has worked.
Update: Some hours later, I found that with the HTMLSerializer I can add HTML code to the span tag. But this is regular HTML, I cannot access to Vue methods.
#Bruja
I was able to find a solution using a closure. The folks at Prismic reminded/showed me.
Of note, per Phil Snow's comment above: If you are using Nuxt you won't have access to Vue's functionality and will have to go old-school JS.
Here is an example where you can pass in component-level props, data, methods, etc... to the prismic htmlSerializer:
<template>
<div>
<prismic-rich-text
:field="data"
:htmlSerializer="anotherHtmlSerializer((startNumber = list.start_number))"
/>
</div>
</template>
import prismicDOM from 'prismic-dom';
export default {
methods: {
anotherHtmlSerializer(startNumber = 1) {
const Elements = prismicDOM.RichText.Elements;
const that = this;
return function(type, element, content, children) {
// To add more elements and customizations use this as a reference:
// https://prismic.io/docs/vuejs/beyond-the-api/html-serializer
that.testMethod(startNumber);
switch (type) {
case Elements.oList:
return `<ol start=${startNumber}>${children.join('')}</ol>`;
}
// Return null to stick with the default behavior for everything else
return null;
};
},
testMethod(startNumber) {
console.log('test method here');
console.log(startNumber);
}
}
};
I believe you are on the right track looking into the HTML Serializer. If you want all your .specialClass <span> elements to trigger a click event that calls specialmethod() this should work for you:
import prismicDOM from 'prismic-dom';
const Elements = prismicDOM.RichText.Elements;
export default function (type, element, content, children) {
// I'm not 100% sure if element.className is correct, investigate with your devTools if it doesn't work
if (type === Elements.span && element.className === "specialClass") {
return `<span #click="specialMethod">${content}</span>`;
}
// Return null to stick with the default behavior for everything else
return null;
};

Enzyme Click on Button Inside Component Inside Component

Im working on a React Javascript Storybook right now and Im trying to figure out how to get enzyme to click a button inside of a component inside a component. So in other words this is the structure
<Component 1>
<Component 2>
<Button>
</Component 2>
</Component 1>
And I want to click on the Button inside of compoenent 2. So far I have this
storesOf("Press the button",module).add("Button press:,() => {
let output;
specs(() => describe('clicked',function () {
it("Should do the thing",function () {
output = mount(<Component 1/>);
output.find("Button").at(0).simulate("click")
})
}));
const Inst = () => output.instance();
return <Inst/>;
});
Does anyone have any advice? I should also add that as of current it does not find any buttons to click
As per Enzyme documentation you can use a React component constructor as your selector. You can do something like this:
output = mount(<Component1 />);
output.find(Component2).find(Button).simulate("click")

Categories

Resources