How to change only render of a React component - javascript

I want to use react-id-swiper library which exports a component named Swiper.
This is its render method:
render() {
const { containerClass, wrapperClass, children, rtl } = this.props;
const rtlProp = rtl ? { dir: 'rtl' } : {};
return (
<div className={containerClass} {...rtlProp}>
{this.renderParallax()}
<div className={wrapperClass}>
{React.Children.map(children, this.renderContent)}
</div>
{this.renderPagination()}
{this.renderScrollBar()}
{this.renderNextButton()}
{this.renderPrevButton()}
</div>
);
}
}
This component perfectly matches my needs, except that I need to place pagination in an outer place (outside of the containerClass element).
One possible solution is to inherit Swiper class and change only it's render method. However Facebook docs are explicit to not use inheritance and use composition instead.
What's the best way to move the pagination outside of containerClass?
Can it be done with composition?

As it stands there is not much you can do, since this component is render specifically in this structure align with a secondary library. In the rebuildSwiper function you can see it passing it the dom elements to this other library to bind and handle all interactions.
Depending on the flexibility of the secondary app you could move the render order. To do so I would first evaluate what the capabilities are of the inner library, then fork this repo and publish updates using the a scoped package such as #<username>/react-id-swiper.

I think composition would be well and good, but because you're using a library and not able to design the source class, I think inheritance is your only option.
I would recommend simply extending the Swiper class. You can write your new render method in a way that would allow for composition reuse moving forard if you want to follow these recommended practices, but I think it's safe to trust your gut in this case.
I'll be watching this question and am curious to see how others would approach it and what you're ultimate take is, too.

Related

Call a function on a react child functional component from parent

I have a very large and complex React application. It is designed to behave like a desktop application. The interface is a document style interface with tabs, each tab can be one of many different type of editor component (there are currently 14 different editor screens). It is possible to have a very large number of tabs open at once (20-30 tabs). The application was originally written all with React class components, but with newer components (and where significant refactors have been required) I've moved to functional components using hooks. I prefer the concise syntax of functions and that seems to be the recommended direction to take in general, but I've encountered a pattern from the classes that I don't know how to replicate with functions.
Basically, each screen (tab) on the app is an editor of some sort (think Microsoft office, but where you can have a spreadsheet, text document, vector image, Visio diagram, etc all in tabs within the same application... Because each screen is so distinct they manage their own internal state. I don't think Redux or anything like that is a good solution here because the amount of individually owned bits of state are so complex. Each screen needs to be able to save it's current working document to the database, and typically provides a save option. Following standard object oriented design the 'save' function is implemented as a method on the top level component for each editor. However I need to perform a 'save-all' function where I iterate through all of the open tabs and call the save method (using a reference) on each of the tabs. Something like:
openTabs.forEach((tabRef) => tabRef.current.save());
So, If I make this a functional component then I have my save method as a function assigned to a constant inside the function:
const save = () => {...}
But how can I call that from a parent? I think the save for each component should live within that component, not at a higher level. Aside from the fact that would make it very difficult to find and maintain, it also would break my modular loading which only loads the component when needed as the save would have to be at a level above the code-splitting.
The only solution to this problem that I can think of is to have a save prop on the component and a useEffect() to call the save when that save prop is changed - then I'd just need to write a dummy value of anything to that save prop to trigger a save... This seems like a very counter-intuitive and overly complex way to do it.... Or do I simply continue to stick with classes for these components?
Thankyou,
Troy
But how can I call that from a parent? I think the save for each component should live within that component, not at a higher level.
You should ask yourself if the component should be smart vs dumb (https://www.digitalocean.com/community/tutorials/react-smart-dumb-components).
Consider the following:
const Page1 = ({ onSave }) => (...);
const Page2 = ({ onSave }) => (...);
const App = () => {
const handleSavePage1 = (...) => { ... };
const handleSavePage2 = (...) => { ... };
const handleSaveAll = (...) => {
handleSavePage1();
handleSavePage2();
};
return (
<Page1 onSave={handleSavePage1} />
<Page2 onSave={handleSavePage2} />
<Button onClick={handleSaveAll}>Save all</button>
);
};
You've then separated the layout from the functionality, and can compose the application as needed.
I don't think Redux or anything like that is a good solution here because the amount of individually owned bits of state are so complex.
I don't know if for some reason Redux is totally out of the picture or not, but I think it's one of the best options in a project like this.
Where you have a separated reducer for each module, managing the module's state, also each reducer having a "saveTabX" action, all of them available to be dispatched in the Root component.

Is it possible to lazy render component in StencilJS?

As you want to build complex component, it would be great if you can wrap any DOM with component such as "lazy-load" component with condition (#Prop() condition: boolean) so to illustrate what I want:
<lazy-load condition={some boolean condition, like certain link get clicked and section is now active}>
<data-fetch>
</data-fetch>
</lazy-load>
in this example, "data-fetch" will make a HTTP call to grab some large data, and I want to defer this component added to DOM until condition we specify in the lazy-load component to be true.
So I started to implement render() of lazy-load component as something along the line of
#Prop() condition: boolean;
render() {
if(!this.condition) {
return null;
}
return (
<slot/>
);
}
and try to use it as
<lazy-load condition={false}>
<data-fetch>
</data-fetch>
</lazy-load>
but no matter what I tried, data-fetch component get added to DOM (and while we can set visibility to hide element, we would waste HTTP call) I understand I can put the same condition in the data-fetch itself and then not make a fetch call when condition is false, but if possible I want generic wrapper component to achieve this (if you are familiar with AngularJS or Angular, I want to find a way to do equivalent of ng-if and *ngIf off of generic wrapper component)
Maybe this is a limitation due to how "slot" tag supposed to work? (also, I'm using it with #Component({shadow: false}) so I know I'm not using standard shadowDOM from the web component spec so maybe what I'm trying to do is not feasible?
Thank you very much for your time in advance to even read this question and I appreciate any help I can get. I feel if we can do this, we might be able to build component that can quickly differ loading until whenever we feel it should load/render.
Yeah it's an issue with not using shadow: true, because in the polyfill the slotted content just becomes part of the light DOM (but gets placed where the slot element is). Beware that even if you enable Shadow DOM, it'll still fallback to the polyfill if the browser doesn't support it. You could raise an issue about this in Github but I'm not sure if/how it would be possible to solve this "dynamic slot" problem.
But I think you can take a simpler approach:
{myCondition && <data-fetch />}
That way the data-fetch element will only be added once the condition becomes true.
You could also refactor this into a functional component:
import { FunctionalComponent } from '#stencil/core';
interface Props {
if: boolean;
}
export const LazyLoad: FunctionalComponent<Props> = ({ if }, children) =>
if && children;
import { LazyLoad } from './LazyLoad';
<LazyLoad if={condition}>
<data-fetch />
</LazyLoad>

Is it possible to achieve something like HOC with classes - without classes in React?

I want to do something like:
<SomeProvider showConfirm={showConfirm}>
{props.showConfirm()
? (<confirmActionComponent />)
: (<chooseActionComponent />)}
</SomeProvider>
Inside of chooseActionComponent I want to be able to access showConfirm or another value in a deep nested child component to update some value in the parent and have confirmActionComponent show.
I know how to achieve this using class which tends to involve this and bind at some point, and I would prefer to avoid that.
Is there any way to accomplish something like this using pure functions/components instead? Would also prefer to keep this out of Redux store.
If you just want to access showConfirm, you simply can pass it to the child:
<SomeProvider showConfirm={showConfirm}>
{props.showConfirm()
? (<confirmActionComponent />)
: (<chooseActionComponent showConfirm={showConfirm} />)}
</SomeProvider>
Note following quote from React docs to inheritance:
At Facebook, we use React in thousands of components, and we haven't found any use cases where we would recommend creating component inheritance hierarchies.
Anyway, I maybe have a really really dirty hack for you...
use ref...
const Child = () =>
<div ref={(self) => {
// get ReactDOMNode on stateless component
const ReactDOMNode = self[Object.keys(self).filter((key) =>
/__reactInternalInstance/g.test(key))[0]];
// access parent props
console.dir(ReactDOMNode
._hostParent
._currentElement
._owner
._currentElement
.props);
}}></div>;
Note: that this is not recommended and I won't recommend that, too.
I would advice you to simply pass needed parent props to the child.
<SomeProvider showConfirm={showConfirm}>
{props.showConfirm()
? (<confirmActionComponent />)
: (<chooseActionComponent showConfirm={showConfirm} />)}
</SomeProvider>
And in your chooseActionComponent you can:
const chooseActionComponent = ({parentProps: {showConfirm}}) =>
<div>{showConfirm}</div>;
You do not have to use ES6 classes to create React content. If you would like to avoid having to repeatedly use bind to ensure correct scoping of methods (that use this.setState / this.props), you can revert back to using the React API helper functions (see React without ES6).
You can specifically use: React.createClass for creating React classes and HOCs. Again, just to re-iterate this: using this alternative syntax will autobind this for you.

What's the difference between `React.createElement(...)` and `new MyComponent()`?

Intro: I'm a bit confused with React. I've seen articles saying that React components are just functions that receive the props and render to the virtual DOM. What I see, however, is that they are full-blown stateful monsters and I have found no way to treat them like functions.
The question: Why is each usage of a React component wrapped in React.createElement? Why can't I use new MyComponent() instead? It looks pretty similar when I do it in DevTools. Why is React.createElement needed at all, given that components are created using React.createClass? It looks like redundant to me.
Edit: this looks relevant: https://gist.github.com/sebmarkbage/ae327f2eda03bf165261
Edit #2: This is related, but not a duplicate of React.Component vs React.createClass, that question asks about creating classes. I'm not asking about creating new component classes, I'm asking about creating instances (elements) of that classes.
I think I found the answer here:
In React 0.12, we're making a core change to how React.createClass(...) and JSX works.
(...)
Currently var Button = React.createClass(...) does two things. It
creates a class and a helper function to create ReactElements. It is
essentially equivalent to this:
class ButtonClass { }
function ButtonFactory(...args) { return
React.createElement(ButtonClass, ...args); }
module.exports = ButtonFactory; ```
Then you access this in the consuming component by invoking the
ButtonFactory.
var Button = require('Button');
class App { render() {
return Button({ prop: 'foo '}); // ReactElement
} }
Conceptually this is the wrong model. The source component should not
be responsible for the output of App.
There are a few problems with this:
ES6 classes can't be directly exported, they need to be wrapped.
There's no convenient way to access the actual class and it's confusing which one you're using.
Static methods are wrapped in helpers that are not real function. As a convenience.
Auto-mocking destroys the factory so there is no way to test the result of render without disabling mocking.
Factories can be wrapped by other factories that returns something different than ReactElements. Making testing and optimizations
impossible.
Languages with specialized features for object management have to defer to React instead of using the built-in features.

React.js: Is it possible to namespace child components while still using JSX to refer to them?

So let's say I have a component called ImageGrid and it is defined as below:
window.ImageGrid = React.createClass({
render: function() {
return (
<div className="image-grid">
<ImageGridItem />
</div>
);
}
});
As you can see it includes a child react component called ImageGridItem. Which is defined below.
window.ImageGridItem = React.createClass({
render: function() {
return (
<div className="item-container">something</div>
);
}
});
This works fine as long as both are directly properties of window. But this is kind of horrible so I'd like to group up all my react components under a namespace of window.myComponents for example.
So the definitions change to:
window.myComponents.ImageGrid = React.createClass({...});
window.myComponents.ImageGridItem = React.createClass({...});
The problem now is that as ImageGrid refers to <ImageGridItem /> in it's render() function, the JS version of this gets compiled out to JS as ImageGridItem() which of course is undefined since it's now actually myComponents.ImageGridItem() and react complains it can't find it.
Yes I realise I can just not write JSX for that component include and manually do myComponents.ImageGridItem({attr: 'val'}) but I'd really prefer to use the JSX html syntax shortcut as it's much easier to read and develop with.
Are there any ways to get this to work while still using the <ImageGridItem /> syntax for children? And if not is it possible to define the JS namespace within the JSX?
This pull request was just merged:
https://github.com/facebook/react/pull/760
It allows you to write things like <myComponents.ImageGridItem /> in React 0.11 and newer.
That said, a proper module system is the recommended way to manage dependencies to avoid pulling in code that you don't need.
Currently, there isn't a way to do this. Namespacing with JSX is on the todo list.
Most people use some kind of module system (browserify, webpack, requirejs), which replace namespacing and allow components to be used easily. There are a lot of other benefits, so I very much recommend looking into it.

Categories