How do I get the wrapped component from ProxyComponent? - reactjs

When using material-ui(^1.0.0-beta.24) and adding JSS Styles to my component as such:
class Counter extends Component{
getCount= () => {
return this.state.count;
}
}
export default withStyles(stylesJss)(Counter);
Accessing the counter component via the "ref" prop in the parent component like:
<Counter ref={(ref) => this.counter = ref} />
this.counter results in a ProxyComponent object instead of the underlying Counter class due to the withStyles wrapper. I would like to access the Counter class and it's method(s) like such: this.counter.getCount() from the parent and use it as a standard React uncontrolled component. How can this be obtained?

You can access the wrapped components ref via the innerRef property
<Counter innerRef={(ref) => this.counter = ref} />

Related

React- How to pass props between two different Microfrontent component

I am using microfrontend, In that I have a main container which uses these two Microfrontend component, and these three component are in different repo.
I have one React component in one repo:
class CustomForm extends React.Component {
...
render() {
return (
<div>
{this.props.component}
</div>
);
}
}
And I have a different component In another repo(other Microfrontend component) that use it:
class SomeContainer extends React.Component {
render() {
let someObjectVariable = {someProperty: 'someValue'};
return (
<CustomForm
component={<SomeInnerComponent someProp={'someInnerComponentOwnProp'}/>}
object={someObjectVariable}
/>
);
}
}
Is there a way to pass props between two different Microfrontend component?
Your proptype for component in CustomForm will need to accept ReactNodes.
But you should be able to pass it through as a function (Meaning without the < and />:
if you do not need to pass any custom props, you can just call
<CustomForm component={SomeInnerComponent} .... />
If you need the custom props, you can wrap it in another function and just pass that through:
<CustomForm component={ () => {
return <SomeInnerComponent someProp={'someInnerComponentOwnProp'}/>;
}} ...... />
The reasoning behind this is that because Functional components in React are actually just Functions, you can pass through a component as a prop without the < or /> and then it will just execute that as a function.

Spreading a component's props across two different child components in React

I am trying to create a custom email input component (for a form) that wraps a Material-UI TextField component inside a custom gridding component that I made. Ideally, I would like to be able to pass any TextField prop I want into this component and have it applied to the inner TextField component by spreading the props, but I also would like to be able to pass any props for the custom gridding component and apply them to the grid component also via spreading.
Example (where variant is a TextField prop and width is a CustomGrid prop):
// CustomEmailField.tsx
...
export const CustomEmailField: React.FC<TextFieldProps & CustomGridProps> = (props) => {
return(
<CustomGrid {...props as CustomGridProps}>
<TextField {...props as TextFieldProps} />
</CustomGrid>
);
};
// index.tsx
...
const App = () => {
return(
<>
<h1>Enter your email</h1>
<CustomEmailField variant={'outlined'} width={2} />
</>
);
};
However, when spreading the props for the gridding component, I get an error message saying that the TextField props (variant in this example) do not exist for this gridding component, and likewise that the gridding component's props (width in this example) don't exist for the TextField component.
What would be a good way to solve this issue so that I can still have flexibility over the props I pass in to each (child) component without having to hardcode what props can be accepted by the email (parent) component?
Just create a new props type.
export type CustomEmailFieldProps = {
textField: TextFieldProps;
customGrid: CustomGridProps;
}
export const CustomEmailField: React.FC<CustomEmailFieldProps> = ({textField, customGrid}) => {
return(
<CustomGrid {...customGrid}>
<TextField {...textField} />
</CustomGrid>
);
};
To use just create an object of the props you want to pass to each.
// index.tsx
...
const App = () => {
return(
<>
<h1>Enter your email</h1>
<CustomEmailField textField={{variant: 'outlined'}} customGrid={{width: 2}} />
</>
);
};

How to create a new ref for each component instance

How to create a ref for each instance of a component
I've extracted some code into it's own component. The component is a PlayWhenVisible animation component that plays/stops the animation depending on whether the element is in view.
I'm creating a ref inside the component constructor but since I'm getting some lag when using 2 instances of the component I'm wondering if I should create the refs outside the component and pass them in as props or whether there's a way to create a new instance for each compoenent instance.
import VisibilitySensor from "react-visibility-sensor";
class PlayWhenVisible extends React.Component {
constructor(props) {
super(props);
this.animation = React.createRef();
this.anim = null;
}
render() {
return (
<VisibilitySensor
scrollCheck
scrollThrottle={100}
intervalDelay={8000}
containment={this.props.containment}
onChange={this.onChange}
minTopValue={this.props.minTopValue}
partialVisibility={this.props.partialVisibility}
offset={this.props.offset}
>
{({ isVisible }) => {
isVisible ? this.anim.play() : this.anim && this.anim.stop();
return (
// <div style={style}>
<i ref={this.animation} id="animation" className={this.props.class} />
);
}}
</VisibilitySensor>
);
}
}
The issue was caused by the VisibilityChecker component which was overflowing the container and causing it to be erratic when firing.

React HOC, instanceof, wrapped with?

I use HOC and the compose method from Redux to wrap a component in components. A simple example :
const Textinput = class Textinput extends Component {
...
}
export default compose(
Theme,
Validator,
)(Textinput);
The result rendered by react is :
<theme>
<validator>
<textinput />
Imagine another component with this children :
<Textinput />
<span>Test</span>
<Select />
<br/>
Textinput and Select are wrapped in the Theme/Validator components with HOC. I want to enumerate each element in this component :
const children = React.Children.map(this.props.children, (child) => {
}
But :
how to know if a rendered element is wrapped in a specific component (like Validator or Theme) ?
how to know if the component behind an element is an instanceof Select or Textinput (and not Theme) ?
Another solution is to add a new property on each HOC object like :
Theme.innerInstances = {
instance: WrappedComponent,
innerInstances: WrappedComponent.innerInstances,
};
Validator.innerInstances = {
instance: WrappedComponent,
innerInstances: WrappedComponent.innerInstances,
};

Accessing refs in a stateful component doesn't work in React?

I'm currently trying to refactor the simple-todos tutorial for meteor using presentational and container components, but ran into a problem trying to access the refs of an input in a functional stateless component. I found out that to access refs, you have to wrap the component in a stateful component, which I did with the input.
// import React ...
import Input from './Input.jsx';
const AppWrapper = (props) => {
// ... more lines of code
<form className="new-task" onSubmit={props.handleSubmit}>
<Input />
</form>
}
import React, { Component } from 'react';
This Input should be stateful because it uses class syntax, at least I think.
export default class Input extends Component {
render() {
return (
<input
type="text"
ref="textInput"
placeholder="Type here to add more todos"
/>
)
}
}
I use refs to search for the input's value in the encompassing AppContainer.
import AppWrapper from '../ui/AppWrapper.js';
handleSubmit = (event) => {
event.preventDefault();
// find the text field via the React ref
console.log(ReactDOM.findDOMNode(this.refs.textInput));
const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
...
}
The result of the console.log is null, so is my Input component not stateful? Do I need to set a constructor that sets a value for this.state to make this component stateful, or should I just give up on using functional stateless components when I need to use refs?
or should I just give up on using functional stateless components when I need to use refs?
Yes. If components need to keep references to the elements they render, they are stateful.
Refs can be set with a "callback" function like so:
export default class Input extends Component {
render() {
// the ref is now accessable as this.textInput
alert(this.textInput.value)
return (
<input
type="text"
ref={node => this.textInput = node}
placeholder="Type here to add more todos"
/>
)
}
}
You have to use stateful components when using refs. In your handleSubmit event, you're calling 'this.refs' when the field is in a separate component.
To use refs, you add a ref to where you render AppWrapper, and AppWrapper itself must be stateful.
Here's an example:
AppWrapper - This is your form
class AppWrapper extends React.Component {
render() {
return (
<form
ref={f => this._form = f}
onSubmit={this.props.handleSubmit}>
<Input
name="textInput"
placeholder="Type here to add more todos" />
</form>
);
}
};
Input - This is a reusable textbox component
const Input = (props) => (
<input
type="text"
name={props.name}
className="textbox"
placeholder={props.placeholder}
/>
);
App - This is the container component
class App extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
const text = this._wrapperComponent._form.textInput.value;
console.log(text);
}
render() {
return (
<AppWrapper
handleSubmit={this.handleSubmit}
ref={r => this._wrapperComponent = r}
/>
);
}
}
http://codepen.io/jzmmm/pen/BzAqbk?editors=0011
As you can see, the Input component is stateless, and AppWrapper is stateful. You can now avoid using ReactDOM.findDOMNode, and directly access textInput. The input must have a name attribute to be referenced.
You could simplify this by moving the <form> tag into the App component. This will eliminate one ref.

Resources