-1

I wrote a component Foo in React.js (its parameter "interface" consists of a single parameter text; there are more parameters in my real code):

import React from 'react';

export default class Foo extends React.Component {
  constructor(props){
    super(props);
  }

  render(){
    return <div>{this.props.text}</div>;
  }
}

and I thought I could improve the code by modifying the property access as:

import React from 'react';

export default class Foo extends React.Component {
  constructor(props){
    super(props)
    this._text = props.text
  }

  render(){
    return <div>{this._text}</div>;
  }
}

This would give me the benefits that

  • I can immediately see what properties are supported by having a look at the constructor.
  • The code that applies the properties gets shorter/better to read.

However, this destroys the update workflow for the property. In a parent component I use Foo like

<Foo text={this.state.parentText}/>

and

this.setState({parentText: "new text"})

does not trigger an update of the text in Foo any more. The constructor of Foo is only called once and therefore, the private variable this._text is not updated on property changes.

=> Using extra private properties to modify the parameter access turned out to be a bad idea.

=> What would you recommend to have a clear interface for the component without breaking the update workflow?

Some ideas:

a) List all used properties at the start of render (and componentDidUpdate)

render(){
    const text = this.props.text;
    return <div>{text}</div>;
}

b) Create a getter for each property and put them directly under the constructor, for example

get _text(){
    return this.props.text;
}

c) (Only for shorter access.) Try to avoid class components. With function components there is direct access with props.text instead of this.props.text. Or, as a workaround, inherit from a custom component class that passes props argument to render:

render_props(props){   
    ...   
}

(Why doesn't react pass this.props as an argument to render by default?)

d) Document the supported properties in a doc string


=> If you know a better option / some standard / best practice, please let me know.

Also tried but failed:

I tried to use state in the child component, with the hope that it would be automatically updated on updates of the parent state:

import React from 'react';

export default class Foo extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      text: props.text
    }
  }

  render(){
    return <div>{this.state.text}</div>;
  }
}

However, this also breaks the update workflow. Using this.state only seems to make sense in the parent component.

Related:

Can I update a component's props in React.js?

https://github.com/vasanthk/react-bits/blob/master/anti-patterns/01.props-in-initial-state.md

Related topic:

How to interact with third party libraries using function components?

https://reactjs.org/docs/integrating-with-other-libraries.html

How do I use/include third party libraries in react?

Integrating React with Other Libraries

Stefan
  • 10,010
  • 7
  • 61
  • 117
  • If you want to add type safety, you could use [TypeScript](https://www.typescriptlang.org/), [flow](https://flow.org/), ... . – c0m1t Nov 21 '22 at 19:57

1 Answers1

1
  • Use function components for React >= 16.8, also see recommendation at https://www.w3schools.com/react/react_class.asp

  • Use useState hooks instead of setState. This is the modern way to write React, and gives you a simpler way to access state (foo.text, foo.setText). https://reactjs.org/docs/hooks-state.html

  • Typescript would help with docs (type props = { text: string }), but I also would like the answer for d) (your question is several questions I think).

  • Use props.text directly, instead of using extra shortcut variable const text = props.text suggested by option a). This way, you don't have a list of all available properties on top of the component function. However, using a consistent props. prefix makes it easier to spot the injected variables in the react code. If there is a huge number of properties and its hard to identify them, try to improve modularization.

JavaScript example code:

Child component Foo:

import React from 'react';

export default function Foo(props){ 
    return <div>{props.text}</div>;
  }
}

Parent component:

import React, { useState } from 'react';
import Froo from './foo';

export default function Parent(){
  const [parentText, setParentText] = useState('Hello world');
  return <Foo text={parentText}/>;
}
Stefan
  • 10,010
  • 7
  • 61
  • 117
IndieJune
  • 148
  • 7
  • If I understand you correctly, you suggest to apply *useState* inside the child component? How is that state updated from the parent component? – Stefan Nov 21 '22 at 20:02
  • Class components are passé – Konrad Nov 21 '22 at 20:10
  • You added part of the solution after you posted the question. Use function components. – IndieJune Nov 21 '22 at 20:12
  • If you're passing the state of a parent to a child via props, the child will update when the parent's state changes. Don't create a new copy of it in a field in the child, just use the prop. Typescript would give `type props = { text: string }`, making it easier to use from the parent. – IndieJune Nov 21 '22 at 20:16
  • Thank you for all the suggestions. I included code in the answer (did not use TypeScript, yet). Please improve example if required. – Stefan Nov 22 '22 at 06:42
  • If class components are passé... Follow up question: What is the function based approach for interacting with other libraries? (I'm currently stuck on how to apply a chart library on a placeholder div created by react and update the chart data via props.) Related: a) https://reactjs.org/docs/integrating-with-other-libraries.html b) https://stackoverflow.com/questions/51939138/integrating-react-with-other-libraries – Stefan Nov 22 '22 at 07:05
  • Related: https://stackoverflow.com/questions/74692822/how-to-use-third-party-libraries-with-react-functional-components – Stefan Dec 05 '22 at 19:43