Add methods to Higher Order Components in React - javascript

From what I understand, HOCs in ReactJS add props to your decorated component, but I want to add methods that can also act on the state.
As an example, I generally never call this.setState() without checking this.isMounted() first. In essence, I want:
export default ComposedComponent => class BaseComponent extends React.Component {
static displayName = "BaseComponent";
constructor(props) {
super(props);
}
//------> I want this method to be available to any ComposedComponent
//------> And it has to act upon the state of ComposedComponent
updateState(obj) {
if (this.isMounted() && obj) {
this.setState(obj);
}
}
render() {
return (
<ComposedComponent {...this.props} {...this.state} />
)
}
}
Say I want to decorate my component Home. So I'd just return it as export default BaseComponent(Home).
But this.updateState() is not available inside Home class. How do I solve this?

Okay, I figured it out. I had spent too much time on this, so I hope this answer could help somebody out as well. Short answer: add the method in your decorator to props, then bind it in your decorated class' constructor.
Here is the code:
export default ComposedComponent => class BaseComponent extends React.Component {
static displayName = "BaseComponent";
constructor(props) {
super(props);
// Note how I am adding this to state
// This will be passed as a prop to your composed component
this.state = {
updateState: this.updateState
}
}
updateState(obj) {
this.setState(obj);
}
render() {
return (
<ComposedComponent {...this.props} {...this.state} />
)
}
}
And here is an example of a class that would use it (I'm using ES7 for simplicity):
#BaseComponent
class Home extends React.Component {
static displayeName = 'Home';
constructor(props) {
super(props);
// And here I am binding to it
this.updateState = this.props.updateState.bind(this);
}
render() {
return (
<div>Hi</div>
)
}
}

Related

React: how to fire the parent component method?

How do I get access from component to the methods of the owner of this component?
I.e. (maybe) you need to get a pointer to the owner of the element and already call the method by the pointer.
For example:
component #1
class MyReport extends Component {
my_method() {}
render() {
return (
<MyReport>
<MyElement />
</MyReport>
);
}
}
and component #2
class MyElement extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
// GET METHOD my_method OF OWNER
console.log(parent.my_method());
}
render() {
return <Button onClick={this.handleClick}>Press</Button>;
}
}
If you are looking how to call a method of the parent you just pass it trough props of the child component:
Component #1
class MyReport extends Component {
my_method() {
}
render() {
return (
<MyReport>
<MyElement methodFromParent={this.my_method}/>
</MyReport>
);
}
}
Component #2
class MyElement extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
// GET METHOD my_method OF OWNER
console.log(this.props.methodFromParent);
}
render() {
return (
<Button onClick={this.handleClick}>Press</Button>
);
}
}
Although, you wrapping MyElement with MyReport seems like a mistake you made when writing this question.

How to pass variables from class to class

Alright, so I'm trying to simplify a project I'm working on, but of all the information I've read on the internet, none of it has answered my question. My doubt is how can I pass variables (the name of the variable, and its value) from one class to another class? Should I use props? Should I just do something similar to this.state.variable? How can it be done? I'll write a sample code just to show what I'm trying to do more visually, however, this is not my actual code. Thanks for helping :)
class FishInSea{
constructor(props){
super(props);
this.setState({fishInSea: 100});
}
render(){
return(
<div>Fish in the sea: {this.state.fishInSea}</div>
);
}
}
class FishInOcean{
constructor(props){
super(props);
this.setState({fishInOcean: <FishInSea this.props.fishInSea/> * 1000});
}
render(){
return(
<div>Fish in the ocean: {this.state.fishInOcean}</div>
);
}
}
export default FishInOcean;
You need to first make both the classes in to React components. Since both the classes modify the state so they called as statefull components. The class has to extend the Base class of React i.e., Component.
in constructor we only do state initialisations but won’t modify the state there. But you are modifying the state which isn’t correct. Instead move setState to componentDidMount.
Say suppose in FishInSea class you have fishInSea and you want to pass it to FishInOcean component as props.
Check below two corrected components how they are passed from one component to the other
import React, { Component } from “react”;
class FishInSea extends Component{
constructor(props){
super(props);
this.state = {
fishInSea: 100
}
}
componentDidMount(){
this.setState({fishInSea: 100});
}
render(){
const { fishInSea } = this.state;
return(
<div>Fish in the sea:
<FishInOcean fishInSea={fishInSea} />
</div>
);
}
}
import React, { Component } from “react”;
class FishInOcean extends Component{
constructor(props){
super(props);
this.state = {fishInOcean: 1000}
}
componentDidMount(){
this.setState({fishInOcean: 1000});
}
render(){
const { fishInOcean} = this.state;
const { fishInSea } = this.props;
return(
<div>Fish in the ocean: {fishInOcean}
{fishInSea}
</div>
);
}
}
export default FishInOcean;
/*There was a typo*/

Running method of component child from this.props.children array

import React from "react";
import ReactDOM from "react-dom";
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
let child = this.props.children[0];
/** Always returns as undefined */
//if (typeof child.childMethod == "function") {
// child.childMethod();
//}
/**
* EDIT: Close, however the this binding seems to not be working. I can however provide the childs props to the childMethod and work with that.
*/
if(typeof child.type.prototype.childMethod == "funciton"){
child.type.prototype.childMethod();
}
}
render() {
return (
<div>
{this.props.children}
<button onClick={this.runMethod}>run</button>
</div>
);
}
}
const App = ({}) => {
return (
<div>
<WrappingComponent>
<NestedComponent />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
So the goal is to have optional methods attached to a nested component that can execute from the wrapping component, almost like an event emmiter. For some reason though, the method that exists on the child component claims not to exist. However whenever I log the child component pulled from the array of the this.props.children the prototype has the method listed.
Am I missing a special way to access methods of children components through a methods variable perhaps?
Found the variable I can use to access it. If anyone has any more insight into this, or reasons why what I am doing is poor practice please let me know.
Editing the question where this is needed, but the item below is accessing the function of the child:
child.type.prototype.childMethod
Does not appear to maintain the this binding. Passing props down does work however.
You should manage all of this logic in the top level component (the App component)
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
render() {
return (
<div>
{this.props.children}
<button onClick={this.props.onClick}>run</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
if (this.nestedComponent) {
this.nestedComponent.childMethod();
}
}
render() {
return (
<div>
<WrappingComponent onClick={this.runMethod}>
<NestedComponent ref={el => this.nestedComponent = el} />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
}
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="example"></div>
Moreover ref with string attribute is deprecated https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs

this.state vs state in React

I'm working in a new codebase. Normally, I would set up state like this in a React component:
class App extends React.Component {
constructor() {
super();
this.state={
foo: 'bar'
}
}
....
In this new codebase, I'm seeing a lot of this:
class App extends React.Component {
state={
foo: 'bar'
}
....
Is there an advantage to doing it this way? They seem to only do it when state doesn't need to be altered. I always thought of state as being something React handled. Is this an ok thing to do?
The end result of both approaches is the same. Both approaches are just setting the initial state of the component. It's worth noting that class properties are a stage 3 proposal, so all development environments may not be able to use them.
I personally like to use the class field variant if nothing else is done in the constructor, as it is less code to write, and you have no super call to worry about.
Example
class Component1 extends React.Component {
state = { value: this.props.initialValue };
render() {
return <div> {this.state.value} </div>
}
}
class Component2 extends React.Component {
constructor(props) {
super(props);
this.state = { value: props.initialValue };
}
render() {
return <div> {this.state.value} </div>
}
}
function App() {
return (
<div>
<Component1 initialValue={1} />
<Component2 initialValue={2} />
</div>
);
}
Actually both of them bind to this pointer. the this that made in constructor of class.
Totally you can access to local state by this.state but in first style you can pass props to constructor by super and then use it in state declaration, just like below:
class App extends React.Component {
constructor(props) {
super(props);
this.state={
foo: 'bar',
jaz: props.someParentState,
}
}
....
Awesome, you can access to props in constructor, isn't pretty? I definitely use this style for local state declaration.
Hope this helps you.

Propagate methods from grand-child component

I have a dumb component, List, that has some methods defined like this:
class List extends React.Component {
...
scrollTo() {
...
}
clear() {
...
}
}
I then use it in a Parent Component, let's say UsersList:
class UsersList extends React.Component {
render() {
return <List {...this.props} {...} />;
}
}
Then I have as a Parent I have FriendsPage:
class FriendsPage extends React.Component {
render() {
return (
...
<UsersList ref={(ref) => { this.usersListRef = ref; }} {...} />
);
}
}
I'd like to be able to call this.usersListRef.scrollTo() for example in FriendsPage, without having to define the methods of List in UsersList.
I can pass a prop called listRef and use it as ref={this.props.listRef} but I was wondering if another solution exists.
You can't call functions of a child and that would also be against the idea of react. Ideally your <UserList> component accepts a prop that makes it know where to scroll to. Something like:
class UserList extends React.Component {
componentDidUpdate() {
const {activeItem} = this.props;
this.scrollTo(activeItem);
}
scrollTo = activeItem => {
// calculate position of active item to scroll to
// and scroll to it
}
}
And then your <FriendsPage> could look something like this:
class FriendsPage extends React.Component {
handleSelectionChange = selected => {
// triggered when the selected element in the list changes
this.setState({selected});
}
render() {
const {selected} = this.state;
return <UserList activeItem={selected} {...this.props} />;
}
}
It's hard to tell if this is 100% the approach you need as you did not provide many details about the conditions that lead to scrolling.
Mmmm, I'm not sure if I'm getting It right, but you should read this: https://reactjs.org/docs/thinking-in-react.html
In React, the idea is to go top-down. As you need the UsersList component to do something when user interacts with List component, then you should define the function in UsersList and pass that function as a prop to the List Component.
For example:
class List extends React.Component {
<div onClick={this.props.scrollTo}
}
I then use it in a Parent Component, let's say UsersList:
class UsersList extends React.Component {
scrollTo(){
do something...
}
render() {
return <List scrollTo={() => this.scrollTo()} {...this.props} {...} />;
}
}
Then I have as a Parent I have FriendsPage:
class FriendsPage extends React.Component {
render() {
return (
...
<UsersList {...} />
);
}
}
I forgot to check the documentation on this one, and there is a paragraph about it here: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components.
Basically, it is the solution I envisaged in my question, using a listRef and passing it down to wherever my List Component is.
Thanks, everyone!

Categories