Render React component from instantiated React.Component - reactjs

I have a couple of React components that are all based of the same base class, these component have a couple of properties which I would like to read before I render the component. This has to do with some conditions that are used somewhere else.
Currently I am calling a method, with something like this in my Render function.
public getWidget(): JSX.Element {
let widget = null;
switch (widgetType) {
case 'widget1': {
widgetComponent = new Widget1(props); // private variable in my class
widget = (<Widget1 { ...props } ref = { some-ref });
}
case 'widget2': {
widgetComponent = new Widget2(props); // private variable in my class
widget = (<Widget2 { ...props } ref = { some-ref });
}
}
return widget;
}
This way I can ask the widget some stuff about it's default values and render the widget variable in my Render function, like this:
render() {
const widget = this.getWidget();
const somethingIWantToKnow = this.widgetComponent.someProperty;
return ({ widget });
}
From what I understand, the reference I set for my React Component is only available after I render? Otherwise I could just use that.
I also tried calling this.widgetComponent.render() in my own Render method, but this does not set up the component correctly (probably because of missing componentWillMount and componentDidMount calls.
I just can't believe this is the way to go, is there a way to render from this.widgetComponent in my Render method, or is there a way to get properties from the class behind a JSX.Element?
NULL checks and other stuff is all removed from these code snippets :)

Give your widget a ref,
widget = (<Widget1 { ...props } ref = { widget1 } />);
Then you can access your instantiated component in componentDidMount and use the ref to access the property,
componentDidMount(){
const somethingIWantToKnow = this.widget1.current.someProperty
}

Related

ReactTS component class: how to save ref on the element

I use TypeScript and class component way and I need to save reference on my element. I am going to get the state of target element when 'resize' event will happen.
Accordingly How to use refs in React with Typescript I tried this:
export default class Carousel extends Component<CarouselProps, CarouselState> {
private ref: React.LegacyRef<HTMLDivElement>;
constructor(...) {
this.ref = React.createRef();
window.addEventListener('resize', (e) => this.resizeCarouselElements());
}
componentDidUpdate(...){...}
componentDidMount(...){...}
resizeCarouselElements = () => {
<...get current state of target element here ...>
}
render() {
return (
<div className='name' ref={this.ref}>
})
}
But after resizing the window I have got null:
How can I save reference on my div className='name' and use it after user's window resizing? Which way is the best practice? Store the ref in state, const or somewhere else?

React, TypeScript - Using non-React objects to inject components into component tree

I'm working on a React Typescript project. A very simplified version of the project is below. I'm trying to use more traditional polymorphism here where I have components returned from vanilla Typescript objects (not React components) that are rendered in the component tree. The reason I want to do this is so that I can have polymorphic classes that I change at runtime and that manage their own state and business logic.
import React, { useEffect } from "react";
class ClickCounter {
private count: number;
constructor() {
this.count = 0;
}
IncrementCount() {
this.count += 1;
}
GetCount(): number {
return this.count;
}
}
interface Operation {
HandleMouseDown(event: React.MouseEvent<HTMLDivElement>): void;
GetComponents(): JSX.Element[];
}
class ClickCounterOperation implements Operation {
private clickCounter: ClickCounter;
constructor() {
const counter: ClickCounter = new ClickCounter();
this.clickCounter = counter;
}
HandleMouseDown(_: React.MouseEvent<HTMLDivElement>): void {
this.clickCounter.IncrementCount();
}
GetComponents(): JSX.Element[] {
const count: number = this.clickCounter.GetCount();
return [<div>you have clicked {count} times</div>];
}
}
export type AppState = {
currentOperation: Operation;
};
export class App extends React.Component<{}, AppState> {
constructor(props = {}) {
super(props);
const initialOperation: Operation = new ClickCounterOperation();
this.state = {
currentOperation: initialOperation,
};
this.HandleMouseDown = this.HandleMouseDown.bind(this);
}
HandleMouseDown(event: React.MouseEvent<HTMLDivElement>) {
console.log("Dispatching mouse down event to current operation");
this.state.currentOperation.HandleMouseDown(event);
}
render() {
return (
<div className="App" onMouseDown={this.HandleMouseDown}>
{this.state.currentOperation.GetComponents()}
<div>some other stuff to show</div>
</div>
);
}
}
export default App;
In the example above everything will render initially, but not after the count is updated. This is because react has no way of knowing that the state has changed and that a rerender is needed. What I'm currently doing is forcing React to rerender by passing down a RefreshOperationState callback to the Operation object that will call the App.setState() method but this feels very ugly and I don't want to do this.
Any way to achieve this kind of traditional polymorphism with React and have non-React objects inject components into the component tree and have the components update when appropriate? I understand what I am trying to do is not following the common React patterns of using Flux/Redux and having all/most app state in a store/s, but I'd like to make this app in a less functional and more OOP pattern where objects store their own state and are called polymorphicly.
Any suggestions?
As you've noted, mixing paradigms might be more trouble than it's worth. React relies on object reference equality to handle its rendering logic. Since you're mutating objects instead of creating new ones, it will never know to update.
Another rule of React state is that it is only data (never behavior and definitely not JSX), and you're trying to use both.
You could make components which use hooks like these, and then let your parent component choose how it composes itself based on what kind of Operation you want.
const useClickCounter = () => {
const [count, setCount] = useState(0);
const incCount = setCount(count + 1);
return [incCount, count];
};
The only other thing I've done is use the observable pattern on the class objects and have a React context in between which observes them and sends the updated state into the React component. The React context provider will cause all consumers beneath it to rerender with fresh state.
public subscribe = (fn: (state) => void) => {
this.observers.push(fn);
}
private update = async () => {
// Give new state to the Context which is subscribed
this.observers.forEach(fn => fn(state));
}
PS: if you're familiar with Redux, you could start with something like this:
const ClickCounter = () => {
const value = useSelector(selectedClickCounter);
return <div>{value}</div>;
};
const operations = {
clickCounter: {
RenderComponent: ClickCounter,
onPressDispatchData: { type: "increment-counter" },
},
};
const OperationHandler = () => {
const [currentOperation, setCurrentOperation] = useState(operations.clickCounter);
return <HandleMouse {...currentOperation} />;
};
const HandleMouse = (props) => {
return (
<div className="App" onMouseDown={props.onPressDispatchData}>
{props.RenderComponent}
<div>some other stuff to show</div>
</div>
);
};

React Context: passing all of a component's methods at once

Suppose I have a container component for handling app logic which has a lot of methods:
class ScreenContainer extends React.Component
{
state = {
inputs: { /* various properties for input values */ },
thingyActive: false,
someList: ["thing1", "thing2"],
// ...etc.
};
handleInputChange = e => {
const { name, value } = e.target;
this.setState(prevState => ({
inputs: { ...prevState.inputs, [name]: value }
}));
};
toggleThingy = () => this.setState(prevState => ({
thingyActive: !prevState.thingyActive
}));
coolMethod = () => { /* Do cool stuff */ };
boringMethod = () => { /* Do boring stuff */ };
// ...more methods...
}
I need ALL of these methods to be accessible to inner components. I'll use a Context provider in this example, and we'll just say that the context gets consumed by various nested presentational components making up a screen in the application.
const ScreenContext = React.createContext();
To pass methods either down to a child component or into a context provider value, it seems you always end up having to do something like below (note that I'm lifting the "actions" into state in this example per the advice given in the React documentation).
class ScreenContainer extends React.Component
{
constructor()
{
super();
this.state = {
// ...same state as before, plus:
actions: {
handleInputChange: this.handleInputChange,
toggleThingy: this.toggleThingy,
coolMethod: this.coolMethod,
boringMethod: this.boringMethod,
everySingleOtherMethod: this.everySingleOtherMethod,
// ...on and on
}
};
}
// ...same methods as before...
render()
{
return (
<ScreenContext.Provider value={this.state}>
{this.props.children}
</ScreenContext.Provider>
);
}
I was looking for a way to avoid passing them all one by one. A possible solution I found involves using a getter and looping through the class instance properties like so:
get allMethods()
{
let output = {};
for (var prop in this)
{
if (this.hasOwnProperty(prop) && typeof this[prop] === "function")
output[prop] = this[prop];
}
return output;
}
Then I can just do:
// (in ScreenContainer constructor)
this.state = {
// ...state,
actions: this.allMethods
};
The getter code could also be extracted out into a utility function for reuse in other container-type components if needed. Obviously, this is only worthwhile if there are a ton of methods to be passed down.
It seems simple enough and appears to work just fine as long as it's done in the contructor. Is there anything crazy about this? Is it bad practice in any way, or does it have any potential side effects I'm not aware of? Is there maybe a better way I'm missing?
EDIT
I've updated the example to be closer to my real code; it now shows what kinds of things the methods might do and uses a Context setup rather than passing the methods down as props to a single child component.
If a class doesn't maintain a state, and class methods are supposed to be used separately as helper functions, they shouldn't be a part of the class, let alone class component. A class acts as namespace in this case. In modern JavaScript, modules are used as namespaces. It can be:
export const coolMethod = () => { /* Do cool stuff */ };
export const coolerMethod = () => { /* Do even cooler stuff */ };
export const boringMethod = () => { /* Do boring but necessary stuff */ };
ScreenContainer component is an example of 'smart' container component. It's always preferable to list passed functions explicitly rather than pass them all automatically. ScreenContainer may get private methods at some point. And there should be a guarantee that lifecycle hooks won't be passed accidentally, too.
If it is supposed to have a single child, it can be applied as higher-order component:
const withScreen(Comp) => {
return class ScreenContainer extends React.Component {
...
render() {
return <Comp handleInputChange={this.handleInputChange} /* ... */ />;
}
}
}
In this particular case render can be distinguished from passed functions because the latter are instance methods (arrow functions). While this kind of magic generally isn't recommended because it may cause problems and won't work properly for private methods, it can be shortened to:
render() {
const fns = {};
for (const method of Object.keys(this)) {
if (typeof this[method] === 'function')
fns[method] = this[method];
}
return <Comp {...fns} {...this.props} />;
}
For multiple children, ScreenContainer children could be traversed to add props in a similar way.
For indirect children, context API can be used to pass functions.
While it's possible to pass ScreenContainer this to children, this isn't recommended because this breaks the encapsulation and contradicts the principle of least privilege.
One way I've done this is to instantiate a new instance in the constructor of the child component like this:
class ChildComponent extends Component {
constructor(props) {
super(props);
this.Container = new MyContainer();
}
Then you can use any methods like:
this.Container.coolMethod()
Edit
I misunderstood. I've only done this by creating a helper class that you instantiate, not a component. It is helpful when you have methods you want to use in multiple components without having to pass all your methods as props through the component tree.

Is it ok to use a wrapper component to pass props in React?

export function injectProps() {
const injects = {store: new Store()}; // some store
return function (Component) {
return class Proxy extends React.Component {
render() {
return React.createElement(Component, {
...injects,
...this.props,
});
}
};
}
}
Is it ok to use this instead of Redux or Context API with React?
Update: I think I missed to point out my expectation. I'm actually passing some service(http, localStorage) to childrens only when they asks for it. It's not only about the store as services don't have any state. But I also need to pass store through it.
https://pastebin.com/G3PgVxLn
Maybe this tweet by the Dan Abramov (React maintainer) might help.
I understand it was probably not the point of the article. But I see
people reaching for Context or Redux because they don’t realize
components can take any children — and that often removes the need for
deep prop passing. Would be great to highlight!
And Dave Ceddia posted a relavant React documentation link.
Composition vs Inheritance
You can read upon those two.
And here is a demo Nicolas Marcora created to show me how to pass properties to child/children.
You can pass props to children using React.cloneElement(child,...
Working demo on StackBlitz.
export default class WithMouse extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = event => { ... }
render() {
const { children } = this.props
const childElements = React.Children.map(children, child =>
React.cloneElement(child, {
mouse: this.state,
onMouseMove: this.handleMouseMove
})
)
return <div>
{ childElements }
</div>
}
}
You can use WithMouse class to pass props downward to all children and use it like following.
class App extends Component {
...
render() {
return (
<WithMouse>
<MouseTracker />
</WithMouse>
);
}
}
MouseTracker has access to props passed from WithMouse so you can just use it without directly passing it manually.
You can probably go further and pass all props instead of a few (mouse, onMouseMove)

Draftjs components with props

I'm new to draftjs and I was wondering if there was a way to render my custom components inline in the editor.
I have a string with twitter handles. I use the decorator to detect regex #[{handle}] which replaces the handle and renders the component inline. However my handle component needs properties such as a callback function and a URL.
I'm not too sure how to pass my component the URL and callback function which I pass into my ContentEditable component.
I'm sure I'm just missing something. I've checked the contentState.getEntity(entityKey).getType() but it only sees the content I pass into the composite decorator as unstyled and not the decorated parts as separate blocks.
I've seen that you can modify the entity map, but I'm not sure if this is the right approach or how to define my own entity in the entity map
Does anyone know what I am missing to give properties to my component?
const decorator = new CompositeDecorator([
{
strategy: handleStrategy,
component: Handle,
},
]);
export default class ContentEditable extends component {
const content = 'some messages and my handle #[handle]';
if (this.props.content.trim() !== '') {
const processedHTML = DraftPasteProcessor.processHTML(content);
const entityMap = processedHTML.entityMap;
const contentState = ContentState.createFromBlockArray(processedHTML.contentBlocks, entityMap);
// Create with content with decorator
editorState = EditorState.createWithContent(contentState, decorator);
} else {
// Create empty content with decorator
editorState = EditorState.createEmpty(decorator);
}
this.state = {
editorState,
}
}
render() {
return (
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
ref="editor"
/>
);
}
I'm sorry the document is missing it. You can provide props in CompositeDecorator like CompositeDecorator({strategy:xxx,component:xxx,props:{...}})
Checking the source

Resources