React Component: Foundation Modal (Reveal) won't open - reactjs

So I've just started learning React and Redux, so I apologize if this is a very noobish question. But I've emptied my Google quote, and can't find anything that helps me.
My problem is: I work on a site with React, Redux, and Foundation 6. One of my React components have a link, that when clicked, should open a Modal with a specific warning for that link. So I've created a component with my modal markup:
ComponentModal.js:
import React, { PropTypes } from 'react';
const Modal = () => (
<div className="reveal" id="exampleModal1" data-reveal>
<h1>Awesome. I Have It.</h1>
</div>
);
export default Modal;
The component with a link renders some stuff, but basically have a
<a data-open="exampleModal1">Click me for a modal</a>
tag in it's render.
And although inspecting the page confirms that the markup for the modal exists, nothing happens when i click the link.
If I move the popup from the component, and into the DOM, the link works.
Some trial and error shows me, that if i manually run $(document).foundation(); in the console when ComponentModal is rendered, the popup works as intended.
So my question is kinda two questions:
1. How do I, in Redux, run $(document).foundation(); when my ComponentModal is done rendering? Maybe I'm wrong, but I don't have the componentDidMount() method available to me, where it might make sense to make that call?
2. Is this a totally wrong way to go about it?
Hope it's not too confusing and not too dumb a question :)

FYI, this question/solution has nothing to do with REDUX!
The problem is that you're trying to use React AND Jquery to make your page do cool things.
You should really choose one or the other.
$(document).foundation() is a Jquery library.
Instead you should use something like React Foundation which removes the jquery dependency and gives you foundation components built with react.

You can do $(document).foundation() in React inside componentDidMount of the component that has the reval modal or better yet You can do this inside componentDidMount of the top most partent in your app.
componentDidMount in React runs once after all the DOM nodes related to that component is mounted. What $(document).foundation() does, if it runs inside componentDidMount, is that it binds event handlers on the elements that have foundation realated attributes like data-dropdown or data-reveal.
componentDidMount() {
$(document).foundation();
}

Related

Alternative to Reactdom.render and unmountComponentAtNode in react18

Important note:
I am aware of createRoot and root.unmount()! Unfortunately (If I understand this correctly) they should be used just once in the application for mounting the react application.
Problem description:
In our app we have a modal component that is rendered dynamically and added to the body of the html via ReactDOM.render(). When this modal is hidden, we unmountComponentAtNode().
Unfortunately, after upgrading to react18, unmountComponentAtNode becomes deprecated and the new unmount is (in my understanding) for the root only. The same problem is about if I try to modify the ReactDOM.Render() for createRoot. Then we would have 2 roots in the app which is wrong.
What is the proper way to attach the modal to the body element (next to root!) and unmount it after it should be destroyed? The implementation is a little bit "weird" (partially in jsx, partially not...) and I would like to avoid refactoring the whole component as there will be a lot of refactoring already in the code... So I would like to focus on refactoring this component (into jsx one) later. Now I have to figure out only the rendering / unmounting. I have been thinking about using Portals, but anyway I have to create that elements somehow and render them into the DOM where portals does not help me a lot.
Calling the createRoot and then render on the root in this modal component fires an error You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it. which is obvious. But there is no "useRoot()" hook or anything like that. Should I store the returned object (root) in some context or somewhere to use it later? Or what should be the best option to call the render? :/
I know how I should do that with classical functional component... But maybe there is some way that I can just refactor a piece of the code instead of the whole component and all its usecases. Maybe there is something I am not aware of (there is definitely thousands of things I am not aware of :D) that should simplify my life...
function modal() {
return (
<div>
...
</div>
)
}
Modal.show = () => {
modalEl = document.createElement('div');
util.destroy(el) => {
ReactDOM.unmountComponentAtNode(el);
el.remove();
}
const childs = props.childs;
REactDOM.render(childs, modalEl);
}
When I was thinking about portals, I thought I will just rewrite the last line of ReactDOM.render to portal like createPortal(childs, modalEl), unfortunately this does not render anything (except modalEl, but no childs inside). The childs are of type ReactNode (using typescript) and they are not empty (because of ReactDOM.render works without any problem).

How do I use custom Javascript in Next js

Coming from React, i am really confused. In pages/index.js, suppose I have a button with onClick listener, and clicking on that button will log "you clicked" in the console. How do i implement this? I want that page to be statically generated and also give that button some functionality.
The reason I am having a lot of trouble is because in React tutorials or even in my projects, if i needed some functionality i'd do this:
function handleClick() {
document.body.style.background = "black"
console.log("you clicked") //nothing is logged in console
}
export default function App() {
return(
<button onClick{() => handleClick}>Click Me</button>
)
}
I was gonna use this Next.js to see how state works. But I encountered a different problem. Unless I use inline function in onClick, it doesnt work. If I use a seperate handleClick function, the DOM doens't even show that I had an onclick event. I learned that's because Nextjs is rendered server side, so it doesnt have access to DOM and console etc. Then how do i do this?
I just transitioned from React, and in every tutorial, those guys would use handleClick func or whatever to handle events and stuff. But I couldnt find a solution to do this in Next, how does everyone handle this then? Because pages have interactive buttons right? Are those pages not statically generated then?
You forgot call function handleClick:
<button onClick{() => handleClick()}></button>
the same way you do it in react with your onClick function
Static generation pre-rendering does not change the interactivity of any page, check the following from Next.js documentation :
Each generated HTML is associated with minimal JavaScript code
necessary for that page. When a page is loaded by the browser, its
JavaScript code runs and makes the page fully interactive. (This
process is called hydration.)
https://nextjs.org/docs/basic-features/pages

react animation with gsap without react-transition-group and ref

I have a question with GSAP and react, as I read from some tutorials, they all use react-transition-group and also many of them use ref as an alternative selector for GSAP how ever, if I use ref in my case, the whole page will be animated, I just want a single element to animate so I use id selector, and it works totally fine like this
import React from 'react';
import { TweenMax } from 'gsap';
import uuid from 'uuid';
import '../styles/homePage.css';
class HomePage extends React.Component{
startAnimation=(pic)=>{
TweenMax.from(`#${pic.id}`, 1, {
opacity: 0,
x: -100,
y: -100
});
}
render(){
const PicsNum = 15;
let pics = [];
let pic = {};
for (let i = 5; i <= PicsNum; i++) {
const picPath = `/pictures/testingPics/${i}.jpg`
pic={id:`a${uuid()}`, picPath}
pics.push(pic)
}
const renderPics = pics.map((p, i) => (
<div
key={i}
className='img-container'
>
<img src={p.picPath} className='pic' id={p.id}/>
<button onClick={()=>{this.startAnimation(p)}}>click</button>
</div>
))
return (
<div className='pics'>
{renderPics}
</div>
)
}
}
export default HomePage;
can someone please tell me why should I use react-transition-group and what can go wrong if I want to use animation without it like I am doing? thank you very much
So, what you are doing here is absolutely fine for simple animations. It's only when your logic and animations start becoming more complicated that you may find it has downsides.
The main problem you may encounter as the complexity of your logic / animation increases is that you actually are now using two different libraries to target the dom. React wants to be completely in control of the dom so it can do its thing. GSAP however also is now looking for an element in the dom and controlling it's state, and react doesn't know about so now things might get out of sync. React might re-render that component, resetting your opacity to 1, when the user has already triggered the fade out.
React-transition-group can be a useful tool in simplifying working with animating components in and out, but it is not the only way to do it or the be all and end all of react animation, so don't feel like you have to use it. Just maybe look into the ways in which is simplifies the code you have to write for every component you want to animate in or out. (It gives you specific lifestyles for animating in and out, and a callback to remove the component post animation, which is the bulk of the boilerplate for component transitions).
In the case of the first issue I mentioned Transition-group is useful here because all your animation code is wrapped within the helpers it provides, so react knows: 1)Your animating... don't do anything till you've finished... 2)now you've finished and I'm back in control.
But there are other options outside of transition group to deal with this dichotomy of dom control:
You can try to be super smart and declarative about it... use refs to access the elements and pass them to gsap animations that are triggered and controlled by state/props.
But there are brilliant libraries that will take all the hassle out of worrying about state and animation and things like https://github.com/azazdeaz/react-gsap-enhancer
This is a wonderful higher order component that just makes sure any changes that gsap makes to the elements are noticed and preserved across react re-rendering and state changes.
Honestly it's a bit magic, and makes working with react and GSAP an absolute pleasure.
Also to answer your question about 'Why refs' instead of the useful 'just pass a string of the ID to the gsap function':
There isn't a right in wrong here. A ref in react will store a pointer to that Dom element in memory. Making it a convenient lookup. Its main advantage is the reference to that element will not expire upon a react re-render. If you manually select an element using GetElementById, and that Dom node is replaced by a react re-render, then your variable reference will become undefined and you'll have to call GetElementById again. GetElementById is very cheap in performance terms, it's not about performance, just avoiding the boilerplate of having to 'find' a new reference to the Dom element after every re-render.

How to load an Angular2 component from AngularJS more than one time?

I am working on an application which has both AngularJS and Angular. Right now, I am invoking Angular component from a html page using the below line:
System.import('app')
Inside app\app.module.ts file, I have specified a specific parent component to be bootstrapped. This parent component in turn invokes a child component. I put some console.log statements in the constructor of the parent and child component and I see everything works fine for the first time.
But, when I navigate away and comeback to the html page again, I notice that the parent and child components are not getting initialized. The only workaround I have found is to refresh the entire page which is not ideal. I tried to unload the Angular components as soon as the user navigates away but I couldn't find any suitable SystemJS methods.
I know Angular components gets initialized only once which is probably why this is happening but is there a way to get past this issue?
Thanks
I have managed to find an answer to my question. The trick is to use the fully qualified URL when importing and deleting the module like below:
System.import('protocol//domainname:portnumber/modulename/filename')
System.delete('protocol//domainname:portnumber/modulename/filename')
Hope this helps someone.
Maybe AngularJS bootstrap interfers with angular2.
https://angular.io/guide/upgrade#bootstrapping-hybrid-applications explains how bootstrap hybrid applications.
You can also try to migrate Angular 2 to Angular 5.
Well, this might be a long shot. But have you tried making your component to implement OnChanges and OnInit. You can call ngOnInit inside ngOnChanges to reinitialize the component again.
//this was my fix for a similar problem
In component, say:
#Component({
selector: 'app-myapp',
templateUrl: './myapp.component.html',
styleUrls: ['./myapp.component.scss']
})
export class MyappComponent implements OnInit, OnChanges {
ngOnChanges() {
ngOnInit(){}; //reinitialize when onchanges is called again
}
}
You should also consider https://angular.io/api/core/OnChanges#usage-notes.
hope it helps

How can I trigger a click on an element inside a child component (that I don't have access to)?

I have installed react-file-base64 into my React JS project and have implemented it like so:
import FileBase64 from 'react-file-base64';
import FileUpload from '../../forms/FileUpload'
...
class MyComponent extends Component {
render() {
return (
<FileUpload buttonText='Upload New Image'>
<FileBase64
multiple={ false }
onDone={ this.changeProfileImage }
/>
</FileUpload>
)
}
}
The code is obviously condensed for brevity.
As you can see, I've wrapped the FileBase64 component inside a custom FileUpload component - to do the old JS/CSS trick of hiding the file upload and triggering it via a different button press.
Given that I do not have direct access to edit the FileBase64 component, since it's been installed by NPM (and will possibly be updated by it in the future), and given that it is not a direct input element but rather a custom component that renders one - how can I trigger a click on the input element rendered by the FileBase64 component, from inside my FileUpload component?
You have a few options.
Reconsider using react-file-base64
This is a pretty minor NPM module, so ask yourself: is it worth using a few dozen lines of someone else's code instead of writing the functionality myself? Open source is amazing and leveraging other people's work can be a lifesaver, but learn to recognize when to lean on it and when not to.
Fork react-file-base64
Fork the original project and add whatever functionality you need to meet your requirements. Ideally do it in a well-written, well-documented way so that you can later open a pull request and contribute back to the project in a meaningful way.
Hack it a bit
It's good to stay inside of React as much as possible, but there are ways around it. You can, for example, still select DOM elements using plain old JavaScript. Remember that stuff? ;P
This would probably work fine - wrap the <FileBase64 /> component in a <div> that you can use to select any nested child <input> elements.
class MyComponent extends Component {
...
onBtnClick() {
this.inputWrapper.getElementsByTagName("input")[0].click();
}
render() {
return (
<FileUpload buttonText='Upload New Image' callback={this.onBtnClick} >
<div ref={(el) => this.inputWrapper = el} >
<FileBase64
multiple={ false }
onDone={ this.changeProfileImage }
/>
</div>
</FileUpload>
)
}
}
I dunno how exactly you're handling <FileUpload /> click callbacks but you get the idea. After a component renders, its DOM elements are laid bare for you to access. The trick is figuring out how to select those elements in the first place, and being careful that you don't break React in the process. But selecting an element and triggering a "click" event is pretty benign.
There are several triggers for this component that maybe suits your needs. Some of them are:
beforeUpload: Triggered before uploading. return true to continue or false to stop uploading.
doUpload: Triggered after the request is sent(xhr send | form submit).
onabort:riggered after you aborting a xhr.
uploadSuccess: Callback when upload succeed (according to the AJAX simply).
If you see the plugin documentation you can be how they work in detail, as well as more different events to interact with your input element inside your FileUpload component.

Resources