i found this issue: https://github.com/facebook/react/issues/4770, and @sophiebits said that React never reuses an instance after it's been unmounted.
does it means that React will never support feature like keep-alive in Vue? or there is other way to maintain component's state?
Can you describe what this feature is for someone who doesnāt know Vue, and how you imagine it working in React?
As Vue's API said:
When wrapped around a dynamic component, \
When a component is toggled inside \
Primarily used with preserve component state or avoid re-rendering.
basic usage:
<keep-alive>
<!-- page-component matched by the route will render here -->
<router-view></router-view>
</keep-alive>
so, with \ It will give users a great experience in some scenarios like news webapp.
+1
keep the component in cache when route to next component,
instead of recreating a new one, reuse the cached component, avoid refetching the data via network to fulfill the component.
In React this is usually solved in one of the two ways:
Keep data cached separately from the component. For example, you can lift state up to an ancestor that doesn't get mounted, or put it in a sideways cache like Redux. We're also working on a first-class API support for this.
Don't unmount the views you want to ākeep aliveā, just hide them with style={{display: 'none'}}
.
componentWillUnmount
@gaearon Could you please explain some details about the first-class API you mentionedļ¼
The second way: the problem is that just hide the element is not enough, some event listeners should be removed, usually implemented in componentWillUnmount
you can also intercept the change (prop/state change) that leads to visible
-> display: none
and do your event listeners clean up easily, and when going from display: none
to visible
attach the listeners again. you are effectively making the component inactive and maintaining component state by doing display: none
, any other use case for keeping an instance alive?
@bjrmatos ļ¼ your suggestion is practical. There are ways to work around. But i think they are not so perfect or clean. since when you write a component, you should concern both the React life-cycle and something like page life-cycle. Doesn't it?
your suggestion is practical. There are ways to work around. But i think they are not so perfect or clean
my suggestion is not a work around, it is the react way of doing things.. reacting to state/props changes and do something is the whole react model. i would say it is clean because it is the way how things should be done with react.
you should concern both the React life-cycle and something like page life-cycle. Doesn't it?
to me what you are describing is just a react component life-cycle concern, a component is a component no matter if it is a whole page or not and you need to use the available lifecycle hooks to do your thing, you can even create a PageContainer
component that has the visible
-> display: none
, display:none
-> visible
logic and never bother with this in your app again.
don't know about the Vue approach but a keep alive feature in react sounds dirty, because there is the traditional way of doing the same with just reacting to changes in normal react lifecycles (componetWillReceiveProps
)
Could you please explain some details about the first-class API you mentionedļ¼
Weāll post it soon at https://github.com/reactjs/rfcs. Stay tuned for PRs there.
@bjrmatos the keypoint is how the PageComponent inform its descendant component to remove event listeners except for all component has a visible propļ¼
Hello @gaearon, I'm a big fan of React butI had to work with Vue at work. I'm working with Vue for months now and I still prefere React. But today I found about keep-alive and for the first time I found something better than React. Just because React does not have this feature. Anyway, I think it's something really increreble and maybe something that React could do too (I don't know).
Here the doc to help you to know more about it: https://br.vuejs.org/v2/guide/components-dynamic-async.html#keep-alive-with-Dynamic-Components
Restrictions like this sound quite artificial:
Note,
<keep-alive>
is designed for the case where it has one direct child component that is being toggled. It does not work if you havev-for
inside it. When there are multiple conditional children, as above,<keep-alive>
requires that only one child is rendered at a time.
We try to avoid introducing APIs in React that work in a very limited subset of cases but then canāt work in others. This might seem āniceā at first but then you need to change the code a little bit, and hit a wall because you have to change the whole approach. Instead, we prefer a more limited set of APIs that work the same way in all circumstances. Then you can learn them once and apply the same techniques everywhere without having to tweak the code whenever you run into a limitation in some convenience shortcut.
The approaches I mentioned above (either lifting state up to a parent component or hiding/showing with style={{display: visible ? 'block' : 'none'}}
) donāt suffer from these limitations and work regardless of how many children you have. The lifecycles are also handled in a predictable way: either you get a full unmount and then re-mount of children (with state preserved in the parent), or the children stay mounted all the way through (and thus donāt need special lifecycle calls). This is consistent with how React works in general and doesnāt require special behavior.
so, with
<keep-alive>
, when user switch between pages, these pages will not be refreshed and their status will be preserved.
This sounds like a recipe for memory leaks to me. If you keep all the components instantiated forever, your app will keep eating memory as you navigate between the pages. Just caching data alone would be okay (and thatās exactly what React lets you do), but keeping component instances and state sounds like it would create problems as the app becomes more complex (but at that point itās too late to fix because the app heavily relies on this pattern). I donāt think weād want to introduce APIs that encourage irresponsible memory usage into React.
That said we definitely want to make the caching use case easier (as a few people rightly noted, brining in something like Redux just to cache network responses is overkill). Weāre working on something for this ā stay tuned for announcements.
Thanks everyone for your links and the discussion! Hope my answer wasnāt too frustrating. I think the āReact wayā might feel annoying if youāre used to having a helper like <keep-alive>
but I also think that having used React a bit more youāll appreciate the explicitness and control that React gives you in this case.
Fair enough.Thank you for your explanation @gaearon. I understand... everything it's trade offs after all.
good
@zengjialuo @gaearon @bjrmatos @ninahaack
I wrote a component using the React.createPortal
API (react-keep-alive), it implements similar functionality to <keep-alive>
in Vue.
Now, I have some doubts about the lifecycle of react-keep-alive. In the first version, I added two lifecycles componentDidActivate
and componentWillUnactivate
, which of course was affected by Vue. In the current version I deleted these two lifecycles and replaced the new lifecycle of the old version with componentDidMount
and componentWillUnmount
.
This can be confusing for users, such as [Lifecycle and events] (https://codesandbox.io/s/q1xprn1qq) and [Control cache] (https://codesandbox.io/s/llp50vxnq7), via `bindLifecycle The life cycle of the component after the high-order function package will be:
Mounting: componentWillMount -> componentDidMount
Unmounting: componentWillUnmount
Activating: componentWillUpdate -> componentDidMount(This is where the user is confused. I am tampering with the life cycle when I activate it.)
Unactivating: componentWillUnmount
I used the life cycle of the old version of React, and it would be confusing if the user used it like this; but the new life cycle may not be a problem, I am not sure.
i hope react can support keep-alive
I have my implementation react-activation and here is the Online Demo
Because React
will unload components that are in the intrinsic component hierarchy, we can extract the components in <KeepAlive>
, that is, its children
prop, and render them into a component that will not be unloaded, drag the rendered content back to the <KeepAlive>
using the DOM
operation
The principle is easy to say but it's not a good idea...The implementation destroys the original rendering level and brings some bad effects.
Simplest Implementation as follows
import React, { Component, createContext } from 'react'
const { Provider, Consumer } = createContext()
const withScope = WrappedCompoennt => props => (
<Consumer>{keep => <WrappedCompoennt {...props} keep={keep} />}</Consumer>
)
export class AliveScope extends Component {
nodes = {}
state = {}
keep = (id, children) =>
new Promise(resolve =>
this.setState(
{
[id]: { id, children }
},
() => resolve(this.nodes[id])
)
)
render() {
return (
<Provider value={this.keep}>
{this.props.children}
{Object.values(this.state).map(({ id, children }) => (
<div
key={id}
ref={node => {
this.nodes[id] = node
}}
>
{children} {/* render them into a component that will not be unloaded */}
</div>
))}
</Provider>
)
}
}
@withScope
class KeepAlive extends Component {
constructor(props) {
super(props)
this.init(props)
}
init = async ({ id, children, keep }) => {
const realContent = await keep(id, children) // extract the children in <KeepAlive>
this.placeholder.appendChild(realContent) // drag the rendered content back using the DOM operation
}
render() {
return (
<div
ref={node => {
this.placeholder = node
}}
/>
)
}
}
export default KeepAlive
I love Vue than React, Vue is so great!!
@bjrmatosēå ³é®ęÆPageComponentå¦ä½éē„å ¶å代ē»ä»¶é¤å»ęęē»ä»¶é½ęåÆč§éå ·ēäŗ件侦å¬åØļ¼
Sometimes it's not a good thing that brother's behavior Vue encapsulates perfectly. The best way to build it is to fit your own framework, isn't it?
Most helpful comment
In React this is usually solved in one of the two ways:
Keep data cached separately from the component. For example, you can lift state up to an ancestor that doesn't get mounted, or put it in a sideways cache like Redux. We're also working on a first-class API support for this.
Don't unmount the views you want to ākeep aliveā, just hide them with
style={{display: 'none'}}
.