How to access dom element that is not inside JSX in React? - javascript

We are using ReactJS framework, so we don't find element written in the entire code. The only way is to find the DOM element and should set the attribute. I am doing this in app.js.
setTimeout(function () {
const formElement = document.getElementsByTagName('form')
formElement.item(0).setAttribute('autocomplete', 'off')
}, 1000)
The above solution is working fine, but if the page loading is slow, then this will not set the attribute. Is there any other way to find the element and set the attribute? I have also tried to set it using the below code after DOMContentLoaded, but it's not working. Nothing inside this event is working.
document.addEventListener('DOMContentLoaded', () => {
const formElement = document.getElementsByTagName('form')
formElement.item(0).setAttribute('autocomplete', 'off')
})

You don't find the element because 'DOMContentLoaded' gets fired when HTML file completes loading, not when React renders the form.
How about using MutationObserver instead? https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Also doing that in root level app.js sounds wrong. Can you do it in the component that is closest to the container of the form?

In React a good way to access DOM elements is by using useRef hook. However, if that element you want to query is not in the JSX of your current component, the useEffect hook could be used with normal JavaScript DOM methods. Like so:
useEffect(()=>{
const formElement = document.getElementsByTagName('form')
formElement.item(0).setAttribute('autocomplete', 'off')
},[]);

Related

React, wait for DOM to update before unmounting?

I have a component that uses a js library. Since it's vanilla js I've added a bunch of dynamic eventListners that I want to remove when unmounting the component. I've set up a function to run on Blur or when clicking out side of the component so it would run document.getElementById and then clone that element and replace it. Right after that I update the state to let Parent component know to not render the component anymore so it unmounts.
What seems to be happening is the code to get, clone the element and replace it isn't happening right away and so the state to unmount is running and by the time the cloning and replacing happens the component i already unmounted so it cannot find that element in the DOM anymore. How can I can avoid this
const cleanUp = () => {
const element = document.getElementById(id);
const clone = element.cloneNode(true);
element.parentNode.replaceChild(clone, element);
setUnMount(true);
};

How to call a method in a child (or any other) component after an event in the parent?

A question like this:
How is it possible in vue3 after an event to call a method in another component?
Found this solution for vue2
this.$refs.myComponent.myMethod()
But how is it supposed to work without "this" in vue3 + composition api?
If this is not possible then what are the alternatives?
The event itself occurs in the following code:
(for example, we can take the resize event of a component and set a task - after it, activate a method inside another component)
<Vue3DraggableResizable class="chartApp"
#resizing="print('resizing')"
>
How is it possible to implement it?
Not sure about if they are best practices but here we go.
I think you can watch your props change and trigger an event like this
import { watch, toRefs } from 'vue'
const props = defineProps({
yourReactiveProp: Boolean
})
const { yourReactiveProp } = toRefs(props) // this is reactive
watch(
() => yourReactivePropn.value,
(newVal) => {
yourMethod() // you can pass the new value also.
})
You can use mitt package. Literally, you can listen and fire emit from anywhere to anywhere. I.e, parent-child, child-parent, siblings, non-siblings, etc..

Access Stripe Payment Element's loading state

In my Reactjs app, I'm using the payment intent API from stripe to handle payments. I use the embeddable Payment Element <PaymentElement /> from #stripe/react-stripe-js to render the UI but the problem is that it takes a couple of seconds before the Payment Element is fully loaded in the UI.
Is there any way I can access its loading state and show a loading spinner while it's being loaded?
Stripe just added a new loader option to their PaymentElement product documented here. It allows you to have a skeleton render first while the UI is loading which should solve the problem you were going for.
Alternatively, you can listen to their ready event documented here so that you show a loading animation until the event is fired.
While this is for their vanilla JS integration, you can use those with their React library since you control which option to pass on the PaymentElement on initialization.
For anyone not content with simply using their loader, you can listen to the ready event (docs).
To do this, you have to get the element first, which is a step that confused me. You should have the elements reference from the useElements hook. In useEffect you can try to do elements.getElement('paymentMethod') but you will get an error saying:
A valid Element name must be provided. Valid Elements are: card,
cardNumber, cardExpiry, cardCvc, postalCode, paymentRequestButton,
iban, idealBank, p24Bank, auBankAccount, fpxBank, affirmMessage,
afterpayClearpayMessage; you passed: paymentElement.
However, the correct thing to get is payment despite not being in that list:
const element = elements.getElement('payment')
element.on('ready', () => {
console.log("READY")
})
Thanks #Yeats on ready solved this for me - great answer.
I want to add for anyone looking at this solution that you should not hide your until the on ready state returns. I mistakenly used a useState variable to show a loader until PaymentElement was ready, but realised that only the loader component needed to be toggled by state and that the PaymentElement should be rendered always. My first try I hid the PaymentElement from render using my loading state var like this:
Don't do this!
{isStripLoading ? (
<MyLoaderComponent />
) : (
<PaymentElement />
)}
So, assuming you have a state variable isStripeLoading default to true and you have useEffect on ready event setIsStripeLoading(false) then wrap only you loader spinner component in the isStripeLoading state variable and NOT the PaymentElement component.
Example
const stripe = useStripe()
const elements = useElements()
const [isStripeLoading, setIsStripLoading] = useState(true)
useEffect(() => {
if (elements) {
const element = elements.getElement('payment')
element.on('ready', () => {
setIsStripLoading(false)
})
}
}, [elements])
return (
<form id='payment-form' onSubmit={handleSubmit}>
{isStripeLoading && <MyLoaderComponent />}
<PaymentElement id='payment-element'/>
<button id='submit' disabled={!stripe || !elements}>Pay</button>
</form>
)

How to insert iframe created using js in a React component

I am trying to build an app with an iframe. It will have controllers to change the iframe style and add text to it. Iframe will be like a preview. I want to create the iframe with javascript. I tried this to create the element and append it to component return div like this:
let iframe = document.createElement('iframe');
document.querySelector('#iframeContainer').appendChild(iframe);
ı received this error :TypeError: Cannot read properties of null (reading 'appendChild')
I also tried another method :
let iframe = React.createElement('iframe', {});
ReactDOM.render(
iframe, document.getElementById('root')
);
in this method, because I don't have a return value, it throws an error. I am not able to insert it in a component.
How should I tackle this problem? I am open to all ideas.
I found a solution to this problem. While I was trying to append the element, I missed out on the component lifecycle. After spending good amount of time here is my solution :
const createIframe = () => {
const iframe = document.createElement('iframe');
document.getElementById('iframeContainer').appendChild(iframe);
}
useEffect(() => {
createIframe();
});
By calling my function in useEffect I made sure that div I wanted to append chil is created. I used useEffect but if you're using class component you should use componenDidMount()

How bad is it to change the DOM in react?

I'm a noob react developer, and I'm currently building a MERN App.
My question to the community is, in my project I had to modify the DOM multiple times as shown below:
document.getElementsByTagName('body')[0].style = 'overflow: hidden';
I know that changing the DOM very often is not recommended in React.Js. So is it ok if I did it only to cut the body's scroll-bar?
In React, changing the DOM directly is usually bad because the state of the page should come directly from the state in React.
But React will render inside the body - the body can't be something returned by the JSX inside a React component, so doing
document.body.style = // ..
really is the only way to change the body's style.
The time you wouldn't want to do such a thing would be if the element being changed was being rendered by React, eg:
// some functional component
const clickHandler = () => {
document.querySelector('.foo').style.backgroundColor = 'green';
};
return (
<div className="foo" onClick={clickHandler}>foo</div>
);
because you could instead toggle some state inside the component which changes the returned JSX to include the different style.
That said, your approach of
document.getElementsByTagName('body')[0].style = 'overflow: hidden';
should be reconsidered, if possible:
Use document.body instead of getElementsByTagName
Do you really have to set the style of the whole <body>? It would be more inline with React to set the style of an element React is rendering, if possible - and then you can use the method mentioned above, of using state and the returned JSX instead of using DOM methods.

Categories