React render component containing only function - reactjs

I'm very very new to react (still learning). One thing I noticed is that component always have a return with HTML (jsx) as content.
But I was wondering if it's possible to create a component containing only functions. For example, in my footer I only have static content. Just plain text and a button to go to the homepage. So this is what I'm trying to do:
index.html
<body>
<main id="app"></main>
<footer id="footer">
<!-- Some other content -->
<button onClick={this.onButton}>Home</button>
</footer>
<script src="/app/bundle.js"></script>
</body>
Footer.js
class Footer extends React.Component {
onButton() {
console.log('Button clicked');
}
render() {
console.log('render');
return null;
}
}
render(<Footer/>, window.document.getElementById("footer"));
This way, the component gets executed and I can see the log, however, the button defined in the index.html is removed.
On the other hand, if I remove the render() from the component, I get this error:
Footer(...): No render method found on the returned component instance: you may have forgotten to define render.
One thing I forgot to mention before, and also based on the item 2 of #aks answer, about using plaing html to create the links is that it's going to reload the whole page if I don't use the React router I'm currently using.
So, for example, If I create a link on the fotter with plain html like this:
Contact
When clicking on the link everything else is going to reset, because the page is going to refresh.
This is why it would be better to just use a function from a component instead of using the whole html declared inside the component.

Nice question. I will answer your question in points:
Render method is a required method, you cannot do without it, it can return null, as you are already doing.
If you say that your footer is static markup and links to some part, You don't even need the js file. It is not required.
In case you have components for most other parts, the static content may look too verbose. You can put those content in the render method of the Footer component as make the index.html very clean. Then in case you need more features in future, you can add them safely.
I hope that helps!

Related

Adding markup on string without dangerouslySetInnerHTML

I have a modal component that receives two strings from a home component, it's easy to take those strings and render them on one single string with the correct markup on the modal.tsx with this
return <>No markup: {firstString}, with markup: <strong>{secondString}<strong><>
But what I actually want to do is render the whole string first in the other component to keep the modal component agnostic and just render whatever string and its markup.
The problem is that on the home component if I store the string with markup on state, I then have to use dangerouslySetInnerHTML on the modal.
Home.tsx
const message = `No markup: {firstString}, with markup: <strong>{secondString}<strong>`
Modal.tsx
<div dangerouslySetInnerHTML={{ __html: message }}>{}</div>
Is there another way to do this, and if not is this actually dangerous?
In terms of directly inlined HTML it is a potential XSS vulnerability.
That's why React, as well as other frameworks, try to make such places explicit and conspicuous.
But in your particular case, you control "inlined HTML" by React:
const message = `No markup: {firstString}, with markup: <strong>{secondString}<strong>`
XSS vulnerability is more about user input.
You have one more option to make "inlined HTML" as safe as possible (including user input) - use Markdown for example)
Gatsby - is a good example of Markdown usage in the React world
https://www.gatsbyjs.com/docs/how-to/routing/adding-markdown-pages/

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

Is it possible to hydrate (or something else) single component?

My case: I have a page preloader, but it makes using react and it's to long (first paint). SSR could solve this problem, but it's too difficult (I mean solving this problem by full SSR).
I want to use something like React.hydrate but for one single component.
I have <MyCustomPreloader /> component which renders <div class="loader" />, but it render with a long delay (after loading the page).
My idea: For example, inside index.html I can make <div class="loader" /> which will be visible at first paint. Main problem say <MyCustomPreloader /> that I have already rendered div and he must use it without creating new.
I could find the necessary DOM inside the component and work with it, but this means abandoning React and continue to work directly with the DOM component.
I tried to manually add <div class="loader" /> into <div id="root"></div> and use React.hydrate instead of React.render and it works! But React.hydrate tries to hydrate every components before and after loader and this solution is kludge.
I believe that there is a function that can partially hydrate a single component (say to component "use this DOM" instead of making same new element), but I cannot find it.
For example:
const loader = ReactDOM.someMagicFunction(<MyCustomPreloader />, document.getElementById("loader"))
Example of this kludge: https://codesandbox.io/s/hopeful-meadow-qgz6j Description: I have "pre-rendered" loader in index.html, and react after loooong loading gets it and USED it (this DOM element).
Can I hydrate single component with some DOM element?

Is there a counterpart to Angular "slots" for DOM nodes in React?

The goal is to create a W3C web component in React which supports arbitrary DOM nodes as children.
The initial markup in the browser should be like this:
<custom-button>
some <u>styled</u> text here
</custom-button>
I would then:
call customElements.define() to register my React code as the implementation of the custom-button,
inside the implementation create a shadow root inside <custom-button>,
afterwards call ReactDOM.render(<CustomButton ...>, shadowRoot); to populate this shadow root
The DOM structure in the browser is now:
<custom-button>
#shadow-root
<div class="CustomButton">
<!-- x -->
</div>
some <u>styled</u> text here
</custom-button>
But this is not really the desired results; I need the original content of <custom-button> to render inside <div class="CustomButton"> now.
I am aware of the React children prop, but as I understand it, it will only work for children that were also declared inside the React implementation and not with arbitrary DOM nodes that were created on the surrounding web component element.
On the other hand I read that Angular implements a concept they call "transclusion" in which they provide slots which DOM children web component will be mapped into. Is there anything similar in React?
One quick "workaround" that I could think of in React is to:
obtain the ref of the top-level JSX tag,
find the shadow root in ref.parentNode
iterate through all children and re-attach them to ref as new parent
This would only cover the initialization though. If, at runtime, some other script tried to append more children to <custom-button>, or re-order or delete previously inserted children, this would probably fail.
I have just realized that the <slot /> of the web components standard solves the problem.
So, to reflect the content of the <custom-element> inside the React component, the following is completely sufficient:
render() {
return (
<div className="customElement">
...
<slot />
...
</div>
);
}

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