Utilizing componentDidMount when switching between different instances of the same React component - javascript

According to the react documentation: http://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount we are adviced use componentDidMount for AJAX call that should be issued when a component is brought into view.
However, when switching between to instances of the same component with different props, componentDidMount is only called for the first component. So what are we supposed to do in this situation?
Currently I have the following workaround: In componentDidMount i do my AJAX call and In componentDidUpdate I compare old and new props to check if I am on a new "instance", and if so I do my AJAX call. But that seems exactly like a workaround. So my question is: is this really the way to do it?
I am aware that I could wrap my component in different empty components to solve my problem. However, this is not possible because we are building a data driven application that uses configurable components and it makes sense to use the same component with different configurations - which is where I'm running into problems.
I am aware that we are actually talking about react elements and not instances as such - witch I guess is part of the problem. Probably I have different react elements utilizing the same instance.
I have made a tiny example to illustrate the react behavior, using plain react (just to make sure I wasn't tricked by react-router or redux and what else we are using the real app):
class Foo extends React.Component {
componentDidMount() {
console.log('componentDidMount ' + this.props.foo);
}
componentDidUpdate() {
console.log('componentDidUpdate ' + this.props.foo);
}
render() {
return <div>Route is {this.props.foo}</div>;
}
}
function navigated() {
ReactDOM.render(
<Foo foo={window.location.hash} />,
document.getElementById('app')
);
}
window.addEventListener('hashchange', navigated, false);
navigated();
Initially when I go to #/bar I get 'componentDidMount #/bar' and when I go to #/baz i get 'componentDidUpdate #/baz'.
I seems like this unanswered question is a specific case of the same issue: React does not know when i render the same component

You can add the key property with unique value for each of hashes:
ReactDOM.render(
<Component hash={hash} key={hash} />, domNode
);
This will update the component every time when the hash is really changed.
https://facebook.github.io/react/docs/multiple-components.html#dynamic-children

TL DR - your 'workaround' looks correct to me
When you initially render the component componentDidMount is called, when you change the hash prop componentDidUpdate is called. It is still the same component, it is just that a specific prop has changed value. In your case, you have logic (running an AJAX call when hash changes) that is specific to your application. React does not known that the hash prop is special, you make is special by adding the logic in componentDidMount. So I believe you have a good interpretation of the React docs and this way of achieving your goal is perfectly valid.

Related

Update react context outside of a consumer?

I am trying to understand how the new react context API works.
In redux, it is possible for a component to have knowledge of dispatch actions without knowing state. This allows updates to redux state without causing a rerender of components that don't care about that state.
For example I could have
<Updater onClick={updateCount}/>
and
<Consumer value={count}/>
Updater is connected to dispatch(updateCount()) and Consumer is connected to count's current value via state.count. When state.count is updated, only the Consumer rerenders. To me, that's a crucial behavior.
In react context, it seems very difficult to duplicate this behavior. I'd like to be able to update state without causing unnecessary rerenders of components that want to alter the context but don't actually care about the state.
How would it be possible for components to trigger updates to context if they are not inside a consumer? And I definitely don't want to trigger an update to the entire tree by setting state at the provider level.
interesting question. Not sure you can without at least an extra layer (but happy to be shown wrong).
Maybe using Memo or PureComponent to minimise the re-rendering?
import React, { memo } from 'react';
function Widget({ setContext }) {
return <button onClick={setContext}/>Click Me</button>;
}
export default memo(Widget);
...
function Wrap() {
const { setSession } = useContext(SessionContext);
return <Widget setSession={setSession} />;
}
One possible solution is to transform your consumer components into pure components and check against the values each component really cares about.
This can be easily done using the onlyUpdateForKeys HOC from recompose.
you can try this library react-hooks-in-callback to isolate the context from your component and pick only desired state values from it,
check this example

does React apply its reconciliation algorithm on consecutive calls to ReactDOM.render?

My question seems trivial at first look, but I could not find an answer to it, so maybe it is not so. Basically I want to use React as a DOM library, with no use of state, i.e. no use of classes. I have top level function component, which takes props, as usual. I render it as recommended through ReactDOM.render. Then somewhere new props will be computed, and I want to rerender the component based on those new props. All the previous similar questions on SO have answers which invariably somewhere use a setState call to do so, but here I do not have classes so there is no this.setState available to me.
What I tried is :
function App(props){
...
}
// using hyperscript here in lieu of jsx
ReactDOM.render(h(App, {..the props..}), document.getElementById('root'));
... later props change, repeat
ReactDOM.render(h(App, {..the props..}), document.getElementById('root'));
My question is : if I do it this way, does React still apply its reconciliation algorithm for efficient DOM update?
Taking a look at the documentation for ReactDOM.render(): https://reactjs.org/docs/rendering-elements.html we can see the following:
React DOM compares the element and its children to the previous one, and only applies the DOM updates necessary to bring the DOM to the desired state.
This is illustrated via an example that calls render() every second:
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
If you run this code with the browser devtools open, you can see that only the updated part of the DOM is updated each time render() is called:
Even though we create an element describing the whole UI tree on every
tick, only the text node whose contents has changed gets updated by
React DOM.
So I think the short answer is: Yes, React still applies the reconciliation algorithm.
Also, from my own experience, I've used a similar model when integrating React components into applications build with other frameworks (e.g. Angular) and observed the same behaviour when updating the React components.

React Hooks - What's happening under the hood?

I've been trying out React Hooks and they do seem to simplify things like storing state. However, they seem to do a lot of things by magic and I can't find a good article about how they actually work.
The first thing that seems to be magic is how calling a function like useState() causes a re-render of your functional component each time you call the setXXX method it returns?
How does something like useEffect() fake a componentDidMount when functional components don't even have the ability to run code on Mount/Unmount?
How does useContext() actually get access to the context and how does it even know which component is calling it?
And that doesn't even begin to cover all of the 3rd party hooks that are already springing up like useDataLoader which allows you to use the following...
const { data, error, loading, retry } = useDataLoader(getData, id)
How do data, error, loading and retry re-render your component when they change?
Sorry, lots of questions but I guess most of them can be summed up in one question, which is:
How does the function behind the hook actually get access to the functional/stateless component that is calling it so that it can remember things between re-renders and initiate a re-render with new data?
React hook makes use of hidden state of a component, it's stored inside a fiber, a fiber is an entity that corresponds to component instance (in a broader sense, because functional components don't create instances as class components).
It's React renderer that gives a hook the access to respective context, state, etc. and incidentally, it's React renderer that calls component function. So it can associate component instance with hook functions that are called inside of component function.
This snippet explains how it works:
let currentlyRenderedCompInstance;
const compStates = new Map(); // maps component instances to their states
const compInstances = new Map(); // maps component functions to instances
function useState(initialState) {
if (!compStates.has(currentlyRenderedCompInstance))
compStates.set(currentlyRenderedCompInstance, initialState);
return [
compStates.get(currentlyRenderedCompInstance) // state
val => compStates.set(currentlyRenderedCompInstance, val) // state setter
];
}
function render(comp, props) {
const compInstanceToken = Symbol('Renderer token for ' + comp.name);
if (!compInstances.has(comp))
compInstances.set(comp, new Set());
compInstances.get(comp).add(compInstanceToken);
currentlyRenderedCompInstance = compInstanceToken;
return {
instance: compInstanceToken,
children: comp(props)
};
}
Similarly to how useState can access currently rendered component instance token through currentlyRenderedCompInstance, other built-in hooks can do this as well and maintain state for this component instance.
Dan Abramov created a blog post just a couple days ago that covers this:
https://overreacted.io/how-does-setstate-know-what-to-do/
The second half specifically goes into details regarding hooks like useState.
For those interested in a deep dive into some of the implementation details, I have a related answer here: How do react hooks determine the component that they are for?
I would recommend reading https://eliav2.github.io/how-react-hooks-work/
It includes detailed explanations about what is going on when using react hooks and demonstrate it with many interactive examples.
Note - the article does not explain in technical terms how React schedule calls for later phases, but rather demonstrates what are the rules that react uses to schedule calls for later phases.
The URL in another answer given by Eliav Louski is so far the best React explaination I have come across. This page should replace React's official tutorial as it removes all the magic from hooks and friends.

How to handle props changes without using componentWillReceiveProps in React

I've been working on a project which is coded with React. I have a component set that I implemented many components for my own requirements. Many of these act like a composite component. For example, TextBox component which has its own label, own error message mechanism and own input filter etc. Moreover, you know, components have props to manage sth.
Everytime to update my component view (render), I use componentWillReceiveProps and I compare the props changes.
But everytime implementing the componentWillReceiveProps method is so repulsive.
Is there any way to pass props from top to down without using componentWillReceiveProps. I don't want to compare props changes manually. Is there any way to do it automatically.
When I change the props in parent, I'd like to update all views just changing the some prop values from top to down.
I'm not an react expert and performance is not my first purpose also!
One more thing that the answer is not use Redux!
I'm waiting your creative approaches and helpful ideas.
Without seeing the code for the particular thing you're working on, I may be missing something about what you're doing...
As others have commented, React will re-render your component if new props are provided, regardless of whether or not you implement componentWillReceiveProps -- the only reason to implement it is to do some kind of specific comparison or set a state based on new prop values.
From the React docs (emphasis mine):
componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
In other words, if you have a component like:
<TextBox title={"Foo"} content={"Bar"} />
That internally passes prop changes on to a couple of child components like:
class TextBox extends React.Component {
render() {
return (
<div className={'text-box'}>
<Title text={this.props.title} />
<Body text={this.props.content} />
</div>
);
}
}
Then each time new props are passed to <TextBox>, <Title> and <Body> will also get re-rendered with their new text props, and there's no reason to use componentWillReceiveProps if you're just looking to update with prop changes. React will automatically see the changes and re-render. And React handles diffing and should fairly efficiently re-render only things that have changed.
However, if you have a separate state value that needs to be set in response to props, for example, if you wanted to show a "changed" state (or whatever) on the component if the new props are different, then you could implement componentWillReceiveProps, like:
class TextBox extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.content !== nextProps.content) {
this.setState({changed: true});
}
}
render() {
const changed = this.state.changed ? 'changed' : 'unchanged';
return (
<div className={`text-box ${changed}`}>
<Title text={this.props.title} />
<Body text={this.props.content} />
</div>
);
}
}
If you're trying to prevent re-render in cases where it's unnecessary for performance, do as Andrey suggests and use shouldComponentUpdate: https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
TLDR; unless you're setting component state from props, there's likely no need to run new props through componentWillReceiveProps
UPDATE Feb 2018: in a future release, React will be deprecating componentWillReceiveProps in favor of the new getDerivedStateFromProps, more info here: https://medium.com/#baphemot/whats-new-in-react-16-3-d2c9b7b6193b
There are few suggestions:
Don't copy props into state in componentWillReceiveProps - just render directly from this.props
If your component need performance tweak (and only if there is problem with performance):
start from using shouldComponentUpdate in generic form like advised here https://facebook.github.io/react/docs/shallow-compare.html
If generic approach doesn't work for you - write custom code
The general approach, how to develop text-box-like components is to keep it stateless.Component renders props directly, and notifies parent component about changes, it don't cares about managing value.
Hope this will help
Please consider pureComponent which by defualt implements the shouldComponentUpdate inside which shallow equals is used for comparison between previous and next
try following codes:
class MyComponent extends PureComponent {...}

Best pattern to rerender my react component?

I have a global data object I update and then I call React.renderComponent() again on the top/main component.
Is this the right pattern for triggering this update?
You should generally pass the data object into the component as a prop, even if it's a global variable. This lets you test the component, and also use it elsewhere without being tied to that global.
As Mike said, there's nothing wrong with using React.renderComponent to update it.
He also mentioned flux, but that's overkill for this. A simple event emitter where you do something like .emit('change', newData), and the component is listening for the change event tends to be better for simpler cases. See my answer to this question for an example of how that can be done.
This is the correct pattern. React.renderComponent will either mount a component for the first time, or get an already mounted component to update.
If you're using a global object though, you might want to look in to the Flux architecture here:
http://facebook.github.io/flux/docs/overview.html
I had the same problem and asked myself if I really needed to re-render the component.
You can do so with this.forceUpdate() but it's not advisable. As React docs states:
You should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.
So what I did was create a data property like exists and test it:
// renderDeleteButton() is being called on render()
renderDeleteButton () {
if (!this.props.store.exists) {
return;
}
return(
<DeleteButton
...
deleteAction={this.delete} />
);
}
Whenever I delete/save, I toggle exists and component will show up or hide based on that. React handles that for me.

Categories

Resources