So I am working on React/redux app with react-router v4.
Page structure is more or less this:
<html>
<head>...</head>
<body>
<aside>
<NavigationComponent />
</aside>
<main>
<ItemComponent />
</main>
</body>
</html>
So I pretty much render an <ItemComponent /> within main depending on the url the user is on, say either /11 or /231. The way I do this currently is use redux's mapStateToProps to retrieve item out of items by id, using react-router injected props, namely params. Then item is passed to as a prop and so the contents get rendered. However, prop changes, namely item changes, only trigger difference rerenders.
How would I accomplish unmounting current <ItemComponent /> and mounting a new one? Or better yet, what's the best way to animate this component, say when entering and leaving (triggered by url change)?
I managed to hack something around using ReactTransitionGroup addon, however I am interested in the cleanest way to accomplish this.
Thanks a lot!
Related
I need to implement components tree on React.
And I'm wondering what is good enough pattern how to break everything in tree
but with scaling(code or team) in mind.
Usually I'm using next way:
function UserAvatar(props) {
return <div><img src="" alt="" /></div>
}
function UserEmail(props) {
return <div>e-mail: some#email.com</div>
}
function UserSection(props) {
return (
<div className="container">
<div className="section">
<UserAvatar />
</div>
<div className="section">
<UserEmail />
</div>
</div>
);
}
But should I avoid all this layout divs and make layout cleaner?
One obvious option is to move into the separate component layout stuff.
Thanks
Guidelines on breaking down components:
When your component is doing too much. Components should ideally be focused on doing one (or a few, but not many) things. The most obvious sign is when the number of lines in the code is getting too long. As a rough estimate, components should not be more than 300 lines.
When you want to reuse components across multiple parent components, you'll have to break them up.
If you want to optimize the render() of specific subtrees of a componant via shouldComponentUpdate(), then you have to break them up as you can only implement shouldComponentUpdate() on the component-level.
Separating data fetching logic from rendering and handling user interactions logic. Instead of making one component do both data fetching and (rendering and handling user interactions), break them down into smaller components so that you can test the components separately:
Test that the first component fetches data.
Test that the second component renders the data properly and can respond to user interactions.
Read more about it here
Many more good answers by Kent C. Dodds here.
It's a good practice to keep each Component in their own files then import them where you need to reference them.
Also, in your example it's most likely you'll need to set the avatar url and email as props. Then make the UserAvatar and UserEmail render the values. Also, it's good practice to setup the propTypes, so other developers (and yourself) know which props to pass.
One criteria to break down components into subcomponents it's when they deal with different concerns, or when the file size is getting too large. Another criteria is when you need to re-use that component in some other context.
Hope this helps.
My issue here is that I need to render separate route components to elements created by the backend. It's irregular I'm sure. Essentially I'm starting with an html document and need to render route components to particular elements in the dom.
Example:
If I have four components that each need to be rendered to a pre-generated element.
<body>
<div id="elone" />
<div id="eltwo" />
<div id="elthree" />
<div id="elfour" />
</body>
Now I need to render my respective components to each of those elements. The issue is that if I call ReactDOM.render within the component it doesn't recognize the router, and it doesn't appear that route has anyway to render to a particular element.
Note: I cannot unfortunately write the document within the JS, it has to be pre-generated. I don't need this to be done through react router if there are other solutions, but the components must recognize the router.
I hate answering my own questions, it makes it seem like no one is really answering questions here.
Anyhow the solution is to use ReactDOM.createPortal instead of ReactDOM.render within the components. Make sure to render the component with the router to an element outside of the container that you want to portal your subcomponents to otherwise you'll obviously clobber the elements that your portals are pointing to.
Is it possible to reuse Vue component which is nested inside parent component, after that parent component is swapped for another component?
My motivation is that I need to have something like "resource component" which is placed in DOM without any re-rendering and to assign it alternately to "consumer components" that make use of its capabilities. To be more specific, in my use case there is one component with <video> element, that is capable to play different sources. And then I have different components for each source, for example: video file, stream, webcam... The main restrictions come from the target platform, which can handle only a limited number of <video> elements and produces unexpected bugs when removing / adding / moving <video> elements in DOM.
Basically, it is very similar to this example from the documentation. But I need to reuse <input> element with this markup:
<template v-if="loginType === 'username'">
<label>Username</label>
<foo>
<input placeholder="Enter your username">
</foo>
</template>
<template v-else>
<label>Email</label>
<bar>
<input placeholder="Enter your email address">
</bar>
</template>
Both parent components (<foo> and <bar>) shouldn't render anything to DOM, while <input> should remain in DOM without any moves or re-rendering or changes in HTMLElement state.
So far I have tried these implementations, but none of them fully satisfies my requirements:
I have tried using key, but it seems to work only for the components with the same name. <foo key="baz"> and <bar key="baz"> won't reuse nested <input>.
I have tried using key on different the components with the same name, but the result remained the same.
I have tried adding undocumented config abstract: true for the <foo> and <bar> components (same as the <template> in the example), but again no reuse of <input>, probably because of the different component names.
I have tried using keep-alive component, but it seems to reuse virtual DOM rather than real DOM, which I need.
I have managed to reuse the <input> component with config functional: true for the <foo> and <bar> components. But this makes the components stateless (no reactive data) and instanceless (no this context), so it's impossible for them to effectively communicate with the <input> component.
I have tried to replace both <foo> and <bar> components with another component <baz>, to make reuse with key working. But I failed to implement some sort of delegation of implementation from <baz> to <foo> and <bar> based on the current requirements.
I am using VueJS 2 to build a drag-and-drop layout builder. One of the requirements of that project is to be able to have some components that will allow for custom content to live inside (they will be just a wrapper around that content). And to be really concrete, I am trying to pass in and render another drag-and-drop zone which is implemented in a draggable component.
Basically, I want to pass a VueJS template to the component via a prop and have that template rendered inside of the component. This is necessary because I do not want the UI to limit the needs of the developer and therefore need this to be really extensible.
In the following trivial example I would like the "ui-element" to render the content prop inside of it and use the other prop as a data input.
<ui-element
:content="<draggable :name="contentData"></draggable>"
contentData="col1"
>
</ui-element>
Since just outputting the template will escape it, and v-html directive will treat it as regular HTML and not a template I am lost, not really sure how to get this done.
I spent about an hour or more googling but no luck. Which leaves me to three options:
1) I'm the first one to need this complex use case (unlikely)
2) Doing this is stupid on so many levels that no-one even bothered (if so, please let me know how to get this result in a smarter way)
3) There is a special uber-cool JS term for this which I simply do not know and that made my search attempts futile
You'd want to use slots instead.
In your ui-element component, define a slot like so:
<template>
<div>
<slot name="content"></slot>
</div>
</template>
Then you could pass in the draggable component like so:
<ui-element contentData="col1">
<draggable :name="contentData" slot="content"></draggable>
</ui-element>
Here's a very basic fiddle example of a slot.
Situation: I have a several input components but depending on the route (react-router) different ones are shown.
Problem: I want the input values the user already entered to be preserved when switching back and forth between routes and dropping/re-adding input components.
I came up with two solutions so far: 1) Always render everything but hide via CSS if not to be shown (breaks the idea of React for me somehow)
2) Implement some kind of "input value store" in the flux architecture (pretty elabortate)
Any other ideas? Am I missing something more fundamental?
I would definitely go for the second approach, something Flux like. But if this is the only thing in your application that needs it, you can keep it very simple. Should be possible to implement in very few lines of code.
The benefit you get is that the Flux approach scales very well, so you won't have to rewrite it if the application grows.
Look at Dynamic Segments
If you have dynamic segments in your URL, a transition from /users/123 to /users/456 does not call getInitialState, componentWillMount, componentWillUnmount or componentDidMount. React-Router-Guide
You should use dynamic routes like this example:
<Route name="inbox" handler={Inbox}>
<Route name="message" path=":messageId" handler={Message}/>
<DefaultRoute handler={InboxStats}/>
</Route>
If you change the messageId(child component) you will still have the same state in the Inbox(parent) Component.
It will look something like this:
<Route name="search" path="/search/:query?/:limit?/:offset?" handler={Search} />