Abstract component vs. prop passing - reactjs

I am building an app for posting tutorials. Two of the components I have are EditTutorialForm and NewTutorialForm. These two components are essentially the same except for the methods componentDidMount and onSubmit.
What seems to make the most sense is to have an abstract component type called TutorialForm and to extend it to make EditTutorialForm and NewTutorialForm.
I have read on the React docs that inheritance is not recommended with React. Would it be "better" to pass the componentDidMount and onSubmit functions as props to the TutorialForm component, as opposed to extending the component itself?

I would create one component and check within something like the following:
For a new tutorial
<TutorialForm edit={false}>
To edit a tutorial
<TutorialForm edit={true}>
And in TutorialForm
class TutorialForm extends Component{
componentDiMount() {
this.props.edit ? do edit stuff... : do new stuff
}
submitForm = () {
this.props.edit ? submit edit... : submit new
}
}

Related

Have a method call another method within a function component

I am using a component called DocumentPicker from the Fluent UI library.
This components has several methods:
<DocumentPicker
removeButtonAriaLabel="Remove"
onRenderSuggestionsItem={SuggestedBigItem as any}
onResolveSuggestions={ /* do some stuff here */ }
onRenderItem={SelectedDocumentItem}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={pickerSuggestionsProps}
disabled={isPickerDisabled}
inputProps={inputProps}
/>
For my specific scenario, I'd like to have a method of this component call another method. For example, have onEmptyInputFocus trigger onResolveSuggestions. How can I accomplish this?
[edit] Basically I am trying to accomplish with a function component what I would be able to do using "this" on a class component. In my class component I could write something like:
public onEmptyInputFocus () {this.onResolveSuggestions();}
Since you specify these methods, it's pretty easy:
const _onEmptyInputFocus = () => {
onResolveSuggestions()
}
<DocumentPicker
removeButtonAriaLabel="Remove"
onEmptyInputFocus={_onEmptyInputFocus}
onRenderSuggestionsItem={SuggestedBigItem as any}
onResolveSuggestions={onFilterChanged}
onRenderItem={SelectedDocumentItem}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={pickerSuggestionsProps}
disabled={isPickerDisabled}
inputProps={inputProps}
/>
I think I am pretty clear now that it cannot be accomplished with function components. You would have to know the internals of the component and tweak it.
A workaround is to use a ref and work with the underlying HTML element. In Fluent UI the prop is actually called componentRef, not just ref.

Can I make a certain function globally available inside a React application?

It is a common practice to pass in the form of a prop, from a root component A, to a subcomponent B, a function that will change the state of A. Like so:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'foo'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
render() {
return (<NameChanger name={this.state.name} onNameChange={this.handleNameChange} />)
}
handleNameChange: function(newName) {
this.setState({
name: newName
});
}
}
Now as you can see NameChanger is one level down only so not a big issue there. But what if it had been down 3 or even 4 levels? We would have had to pass it down the chain of components and that bothers me big time. Is there a way to make a function globally available within the app?
I looked at Context (https://reactjs.org/docs/context.html) but I am not sure it is the right design choice for globally available functions. Or is it?
Thanks
In a typical React application, data is passed top-down (parent to
child) via props, but this can be cumbersome for certain types of
props (e.g. locale preference, UI theme) that are required by many
components within an application. Context provides a way to share
values like these between components without having to explicitly pass
a prop through every level of the tree.
https://reactjs.org/docs/context.html
Try using Redux or Mobx(very easy to start with) as state management library to solve this problem.

Best practice for dynamic routing (react router v4) needed?

With React Router V4 being out only for a little while and there being no clear documentation on dynamic routing [akin to transtionsTo(...) in V3] I feel like a simple answer to this question could benefit many. So here we go.
Lets assume the following theoretical scenario: one has a component Container, which includes two other components (Selection and Display). Now in terms of functionality:
Container holds a state, which can be changed by Selection, Display shows data based on said state.
Now how would one go about changing the URL as well as the state triggered by a change in state via react router?
For a more concrete example please see (React Router V4 - Page does not rerender on changed Route). However, I felt the need to generalize and shorten the question to get anywhere.
Courtesy to [Tharaka Wijebandara] the solution to this problem is:
Have the Container component provide the Selection component with a callback function that has to do at least the following on Container:
props.history.push(Selection coming from Selection);
Please find below an example of the Container (called Geoselector) component, passing the setLocation callback down to the Selection (called Geosuggest) component.
class Geoselector extends Component {
constructor (props) {
super(props);
this.setLocation = this.setLocation.bind(this);
//Sets location in case of a reload instead of entering via landing
if (!Session.get('selectedLocation')) {
let myRe = new RegExp('/location/(.*)');
let locationFromPath = myRe.exec(this.props.location.pathname)[1];
Session.set('selectedLocation',locationFromPath);
}
}
setLocation(value) {
const newLocation = value.label;
if (Session.get('selectedLocation') != newLocation) {
Session.set('selectedLocation',newLocation);
Session.set('locationLngLat',value.location);
this.props.history.push(`/location/${newLocation}`)
};
}
render () {
return (
<Geosuggest
onSuggestSelect={this.setLocation}
types={['(cities)']}
placeholder="Please select a location ..."
/>
)
}
}

In componentDidUpdate refs is undefined

I want to use Chart.js on my website. As you can see title, I'm using React.js. To use Chart.js, I need the canvas and context like this:
let context = document.getElementById('canvas').getContext('2d');
let chart = new Chart(context, ...);
so I design the component like this:
export function updateChart() {
let context = this.refs.chart.getContext('2d');
let chart = new Chart(context ,... );
...
}
export default class GraphChart extends React.Component {
constructor() {
super();
updateChart = updateChart.bind(this);
}
componentDidMount() {
updateChart();
}
render() {
return <canvas ref="chart" className="chart"></canvas>;
}
}
as you can see, I exported two things, update chart function and GraphChart class. Both will using in parent component like this:
import { updateChart } from './GraphChart';
import GraphChart from './GraphChart';
class Graph extends React.Component {
...
someKindOfAction() {
// update chart from here!
updateChart();
}
render() {
return (
<div>
<SomeOtherComponents />
<GraphChart />
</div>
);
}
}
then Parent class using exported updateChart function to update chart directly. It was working, but only first time. After unmount and mount the GraphChart component, it's refs are just empty.
Why refs is empty? And If I did wrong way, how can I get canvas context for initialize Chart.js?
Object refs is undefined, because this is not what you think it is. Try logging it.
The function you’re exporting is not bound to this of your component. Or perhaps it is, but to the last created instance of your component. You can never be sure that’s the mounted instance. And even if you are, you can not use multiple instances at the same time. So, I would dismiss this approach entirely.
Other than that, providing the function to alter some component’s state is exactly the opposite of what’s React is trying to accomplish. The very basic idea is that the component should know to render itself given some properties.
The problem you are trying to solve lies in the nature of Canvas API, which is procedural. Your goal is to bridge the gap between declarative (React) and procedural (Canvas) code.
There are some libraries which do exactly that. Have you tried react-chartjs? https://github.com/reactjs/react-chartjs
Anyways, if you’re wondering how the hell should you implement it the “React way”, the key is to declare properties your component handles (not necessarily, but preferably), and then to use component lifecycle methods (e.g. componentWillReceiveProps and others) to detect when properties change and act accordingly (perform changes to the canvas).
Hope this helps! Good luck!

React with REST API - State or GET on mount?

We're currently building a React-Redux frontend with a REST API backend powered by Node. I'm unsure about whether to use a Redux or a simple call to the API on mounting the component.
The component is a simple list of profiles which are going to be displayed throughout (but not constantly) the site.
Sorry for asking this. Maybe there's something to read through available?
I would advice you to take a look at two things:
1) The first React tutorial on Facebook is very underrated:
https://facebook.github.io/react/docs/thinking-in-react.html
It exposes a very clear way to think about how to think about the tree structure of your views.
2) From there, move to reading about Containers and Components:
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
This post explains that React components too often do two things: act as renderers and as controllers (taking on both the V and the C on MVC).
Now, what your React view needs is a controller. Fetching it whenever you mount the component overlaps two different concerns: how to display the information and how to fetch it.
You could do it with a single, bigger React component that manages the complete state of your application:
class MyApp extends React.Component {
componentDidMount() {
fetch('/profiles').then(res => res.json().then(::this.setState))
}
render() {
if (this.state) {
return <ProfileList profiles={this.state} />
} else {
return <span>Loading...</span>
}
}
}
That would be your "Container". Your "Component" is a pure representation of the list of profiles, that needs not care about how that information was retrieved:
class ProfileList extends React.Component {
render() {
return <ul>
{
this.props.profiles.map(
profile => <li key={profile.id}>{profile.name}</li>
)
}
</ul>
}
}
Redux is just another way of doing this that enables better reuse of information, and makes that same information available to different components (hiding the instance of the "store" as a mixin). That MyApp class on top of your structure serves a similar function to the Provider class in redux: allowing child components to access information needed to display themselves.

Resources