static inside React component - reactjs

Can anyone tell me what happens in line number 9 in the file Wizard.js here: https://final-form.org/docs/react-final-form/examples/wizard ? How does the unwrapping work and stuff like that?
I do understand the concept of children in React, I think, but not in this way. If I change the <Wizard.Page> inside index.js with <div> and remove the line 9 in Wizard.js, the code still work, but not if I just remove the line 9 in Wizard.js.

This line defines a very basic functional component, that simply renders its children.
It is defined as a static property on the Wizard class, so that it is accessible through Wizard.Page.
The code in Wizard.js does not really rely on the fact that the children of the Wizard component are actual Wizard.Page components - it just assumes that the children are the different steps in the wizard:
render() {
const { children } = this.props
const { page, values } = this.state
const activePage = React.Children.toArray(children)[page]
...
}
That's why it still works when you used <div> instead of <Wizard.Page>: the div elements act as wrappers around their children too, and the code in the render() method will still work as intended.
But the <Wizard.Page> approach has several benefits:
It makes the code in index.js more readable and explicit
If needed, the Wizard.Page component could evolve into a more complex component, with additional logic, without requiring any change in index.js

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).

React.forwardRef is already possible without it, so what's the use of it?

I'm confused on the point of React.forwardRef. As explained in its documentation, I understand that its main use is for a Parent Component to gain access to DOM elements of the Child Component. But I can already do that without even having to use it.
Here is a code example that you can plug into CodeSandbox and see that it works:
import React, {useRef, useEffect} from "react";
import "./styles.css";
const ChildComponent = (props) => {
useEffect( ()=> {
props.callbackFunction()
})
return(
<div ref={props.fRef}>
{"hello"}
</div>
)
}
export default function App() {
const callbackFunction = () => {
console.log("The parent is now holding the forwarded ref to the child div: ")
console.log(forwardedRef)
}
const forwardedRef = useRef(null)
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<ChildComponent name="gravy" callbackFunction={callbackFunction} fRef={forwardedRef}/>
</div>
);
}
Or here's the embed of this example. Honestly, I'm kind of new to this and I don't know exactly how embeds work and whether someone fiddling with the embed changes my original Sandbox or not, so I was hesitant to put it. But here it is.
Example Forwarding Ref
In the example, the parent App() component successfully passes a ref to the child which the child attaches to its rendered div. After it renders, it calls a callback function to the parent. The parent then does a console log where it proves that its forwarded ref now has a hold of the child's div. And this is all done without React.forwardRef.
So what then is the use for React.forwardRef?
You're absolutely right that you can do what you've described. The downside is that you're forced to expose an API (ie: the fRef prop) for it to work. Not a huge deal if you're a solo developer building an app, but it can be more problematic eg. if you're maintaining an open-source library with a public API.
In that case, consumers of the library won't have access to the internals of a component, meaning you'd have to expose it for them somehow. You could simply do what you're suggesting in your example and add a named prop. In fact, that's what libraries did before React 16.3. Not a huge deal, but you'd have to document it so people know how to use it. Ideally, you'd also want some kind of standard that everyone used so it wasn't confusing (many libraries used the innerRef naming convention), but there'd have to be some consensus around that. So all doable, but perhaps not the ideal solution.
Using forwardRef, passing a ref to a component just works as expected. The ref prop is already standardized in React, so you don't need to go look at docs to figure out how to pass the ref down or how it works. However, the approach you describe is totally fine and if it meets your needs, by all means go with that.
As mentioned in the docs , it's useful for highly reusable components, meaning components that tend to be used like regular HTML DOM elements.
This is useful for component libraries where you have lots of "leaf" components. You've probably used one like Material UI.
Example:
Let's say you're maintaining a component library.
You create a <Button/> and <Input/> component that maybe just adds some default styling.
Notice how these components literally are just like regular HTML DOM elements with extra steps.
If these components were made to be used like regular HTML DOM elements, then I expect all the props to be the same, including ref, no?
Wouldn't it be tedious if to get the button ref from your <Button/> component I'd have to get it through something like fRef or buttonRef ?
Same with your <Input/>, do I have to go to the documentation just to find out what ref to use and it's something like inputRef ? Now I have to memorize?
Getting the ref should be as simple as <Button ref={}/>
Problem
As you might know, ref will not get passed through props because, like key, it is handled differently by React.
Solution
React.forwardRef() solves this so I can use <Button ref={}/> or <Input ref={}/>.

Are there any benefits of calling an SFC function within the render method?

I am working with the react-gsap library, and want to encapsulate specific <Tween> instances that do not need any props, because they are occuring multiple times in the same way.
Lets take a simple example of an instance, that doesn't need any props or state at all. Let's say this line
<Tween
to={{opacity: 0}}
duration={5}
/>
occurs really often in our code base, and we want to abstract it.
My first idea was to just create an SFC for that:
const HideTween = () => (
<Tween to={{ opacity: 0 }} duration={5} />
);
// and then ...
<HideTween />
but that seems not to work at all. The Tweens are simply not showing any effect.
I then came up with another idea (which i personally dislike) to just call the SFC. Instead of <HideTween /> , we now have
{HideTween()}
and voila, it works...
I am specifically curious now, why my first idea did not work at all. The question is not aimed into the gsap library directly, but more of a general form: Where, when and how can such an approach (of abstracting parts of your render into own functions) fail? If it is the library, how does it even achieve such a behaviour?
And why does it seem to work, when i call the function directly (i know this is kind of a bad approach)? Without any state or props present,
shouldn't <HideTween /> have the same effect on every render as {HideTween()}?
EDIT
Here is a minimal example
Exchange <Tweens /> in line 38 with the content of the Tweens SFC, and you will see the animation again.
You can share the same elements between different render methods by creating them ahead of the render cycle as you tried to do:
const HideTween = <Tween to={{ opacity: 0 }} duration={5} />
and then directly using the element (not instance) in the render methods:
<Timeline>
{HideTween}
</Timeline>
<Component /> is JSX sugar for creating an element of a react component. So your first idea didn't work as it would return an element returning an element of a Tween and not directly the Tween element. For usual DOM rendering this will work as expected, though. The second example works as you get the Tween element this way, but you're right to dislike it as it simply adds an unnecessary indirection.
Doing it this way you might think that react will reuse the same instance of the component in different places, but it will actually instantiate the component anew for each usage. Here is an example:
https://stackblitz.com/edit/react-rzq5q5?file=index.js
I assume this is intentional as sharing the same instance of a component seems to be quite a rare use case and with stateful components the state would be shared as well synchronising components in different parts of the app (which could end up being quite confusing).
Also check out this article on the difference between components, elements and instances (instances will be created by react for you): React Components, Elements, and Instances
Here I've set up a simple example trying out a few things: Example
In your Tween component:
class Tween extends React.Component {
static defaultProps = {
to: {opacity: 0},
duration: 5
}
...
}

Stateless functional component vs. additional render method in a stateful component?

As a really basic example, imagine I wanted to render a div containing some text outside of my render() method. Which of these options would be better?
class StatefulComponent extends Component {
...
render() {
return (
{this.renderTextDiv(this.state.text)}
)
}
renderTextDiv(text) {
return <div>text</div>
}
}
or
class StatefulComponent extends Component {
render() {
return (
<TextDiv text={this.state.text} />
)
}
}
function TextDiv({text}) {
return <div>{text}</div>;
}
Or would you completely pull the stateless component into its own class? Or does it just not matter at all? Does one make testing easier? Readability? Any differences at all?
It does not make any difference in terms of what is being displayed. However, it definitely changes the structure and readability of the code.
For myself, I try to divide the structure into as much Components as possible, and so does Thinking in react suggests. However, if you think that the element does not structurally differ from its parent component and thus does not deserve its own separate component but you require some readability and re-usability, the top code works.
The first one looks more concise in my opinion. You could render html in methods if it's not a big code, or, just a conditional rendering inside the component. Whenever you have some html rendering with a big structure and some functionality, it is always good to separate it into its proper component, because you probably would reuse this code later. Again: If it's not a big code or functionality, it's ok to use html rendering inside a method.

Best practice for conditional rendering of children elements in ReactJs with Redux connect?

The situation I have is a login screen that displays one of 3 blocks of code, depending on the store state of the app. Eg... Below would be the resulting render if the second display option was selected.
<LoginFormPage>
<DisplayOption2 />
</LoginFormPage>
There are specific calls and logic for each display option distinct enough to warrant their own containers. My file structure is:
/components
/displayOpt1.jsx
/displayOpt2.jsx
/displayOpt3.jsx
/loginFormPage.jsx
/containers
/displayOpt1.js
/displayOpt2.js
/displayOpt3.js
/loginFormPage.js
I need a way to render the correct option without embedding too much logic into the parent container; since it really doesn't need to know anything about any of the login mechanisms. I can think of a few ways to do it.
All logic in loginFormPage.js with a connect direct to the loginFormPage.jsx. Then conditional parameters in the loginFormPage.jsx that makes calls to the components directly; removing the other containers.
Create a React.Component in the loginFormPage.js to do the conditional rendering calls to the other containers; this would call all the .jsx files from the container component. loginFormPage.jsx would then render the selected child with {props.children}.
Same as 2. but do the conditional rendering call in the mergeProps parameter passed to connect in loginFormPage.js; rather than creating a jsx component in the container js code.
Some standard practice that I don't know of?
Right now I'm leaning towards option 3, but I can't find any evidence with my Google searches of this being a recommended practice. All thoughts are welcome. Thanks.
Some code to maybe make it easier:
loginFormPage.jsx
<div>
<div onClick={props.someActionHeader}>
<h1>Login Form</h1>
</div>
<div className="formarea">
// render the selected option here based on props.renderOptionChoice
// this will be one of displayOpt1, displayOpt2, displayOpt3
</div>
<div className="otherstuff">...</div>
</div>
displayOpt1.jsx - Opt2.jsx and Opt3.jsx code is a variation of something like this
<div onClick={props.someAction1}>
stuff...
</div>
loginFormPage.js
import LoginFormPage from '../components/loginFormPage'
const mapStateToProps = (state, ownProps) => {
return {
renderOptionChoice: state.login.selectedLoginType,
}
}
const mapDispatchToProps = ...
export default connect(mapStateToProps, mapDispatchToProps)(LoginFormPage)
I can answer with what I've found to be the best practice. It's worth reading the 3 posts in my comment to the question.
The Container level should contain the What is being displayed. In terms of a Login screen with several different options, then all the What's should be presented in one file. Following this style makes it clear exactly What is being displayed on the particular screen / component simply by looking at a single file.
So at the top level, the render will look like:
render() {
return (
<LoginPage>
{this.state.step === STEPS.Step1 && <LoginStep1 />}
{this.state.step === STEPS.Step2 && <LoginStep2 />}
{this.state.step === STEPS.Step3 && <LoginStep3 />}
</LoginPage>
)
}
Here LoginStep1/2/3 can be contained components with their own connection to the Redux state, or it can be managed at the LoginPage level if the steps code is very simple or strongly related.

Resources