Declaring routes using an array - javascript

I want to move my route declarations for React Router to separate files and then complete the route specification automatically, like this:
// Central array for routes
const routes = [];
// Two routes in separate files
routes.push(<Route path="page1" component="Component1"/>);
routes.push(<Route path="page2" component="Component2"/>);
// Render the routes
<Router history={browserHistory}>
{routes}
</Router>
Although this works, I'm getting the warning that all children of the iterator, i.e., the routes, should have a key prop:
Each child in an array or iterator should have a unique "key" prop. But are keys really necessary in this case? From my understanding, the routes render only once and are not dynamic, even though I'm using an array.
Is it possible to do that in React?

Because you are using an expression (instead of hard-coded/pre-configured children) and because the expression is getting evaluated to an array, React thinks that this is a component with dynamic children and hence it starts warning you with that message.
And thinking more about it, it makes sense. Because as far as React is concerned, there's nothing stopping you from modifying that array and expecting the expression to resolve to a different data set. Therefore it thinks it's a component with dynamic children.

From my understanding, the routes render only once and are not dynamic, even though I'm using an array.
Even though <Router> will render only once, it still is a React component and it's not aware that it will "only render once". The component needs to be ready to 're-render' and do so efficiently.
In order to make sure the component is render efficiently, it asks for these "keys".
You can stop reading my response here but if you want to build more intuition around it, keep on reading :)
Imagine you witnessed a thief stealing an old-lady's purse...
The police brings you in to identify the bad guy in a lineup. There are 8 suspects outside the lineup room. The officer brings in the first 6 suspects inside the room. The other 2 are waiting outside due to room capacity issues.
Each one of them is holding an ID tag (the key). You take a good look at each one of them and write down on a piece of paper their IDs. You still need to see the other 2 suspects but by law, the line up room must always have 6 people. Which means 4 of the ones you just saw will remain in the lineup room.
Because you're very smart and wrote down the ID of the suspects you already looked at (look at = render), you can skip looking at the suspects whom's ID is already in your list.
I know it's a weird example but I hope it helps :)
If you want more details, the React docs are a good place to start: https://facebook.github.io/react/docs/reconciliation.html#list-wise-diff

If you are sending props down to the child components without specific ids you would see this warning.
So basically when you say run a loop and pass the parameters to child components you need to specify ids for each iteration in the props.
For example,
const a=[
{'id':1,'name':'x',age:'25'},
{'id':2,'name':'y',age:'26'},
]
Parent Component:-
class Parent extends React.Component{
constructor(){
super();
this.state={
details=[
{'id':1,'name':'x',age:'25'},
{'id':2,'name':'y',age:'26'},
]
}
}
_getDetails() {
return this.state.details.map((content)=>{
return (
<Child key={content.id} name={content.name} age={content.age}/>
);
});
}
}
In above Child is the component where you are passing props to.

One simple solution is to use Plain Routes instead of JSX:
const routes = [];
routes.push({
path: 'path1',
component: Component1
});
routes.push({
path: 'path2',
component: Component2
});
<Router routes={routes} history={browserHistory}/>

Related

Will JSX conditional rendering out of an object code split?

Will conditional rendering out of an object code split and lazy load as expected? Here's a short example of what I'm talking about.
const Component1 = lazy(() => import('some path'));
const Component2 = lazy(() => import('some path'));
const Component3 = lazy(() => import('some path'));
render () {
const { selectionIndex } = this.state;
<Suspense fallback={<div>Loading...</div>}>
{{
one: <Component1 />,
two: <Component2 />,
three: <Component3 />,
}[selectionIndex]}
</Suspense>
}
I want to know whether all three components will load on render, or just the one selected by selectionIndex. I'm trying to use this to conditionally select something to display based on a menu set by state, but I don't want to load everything at once.
They will not get rendered all at once. You can experiment by yourself, put console.log inside components is an easy way to find out.
React for web consists of two libs, "react" and "react-dom". "react" is in charge of encapsulating your logic intention into declarative data structures, while "react-dom" consumes these data structures and handles the actual "rendering" part of job.
The JSX element creation syntax <Component {…props} /> translates to plain JS as an API call to React.createElement(Component, props). The return value of this API call is actually just a plain object of certain shape that roughly looks like:
{
type: Component,
props: props
}
This is the aforementioned "declarative data structure". You can inspect it in console.
As you can see, calling React.createElement just return such data structure, it will not directly call the .render() method or functional component’s function body. The data structure is submitted to "react-dom" lib to be eventually "rendered".
So your example code just create those data structures, but the related component will not be rendered.
seems like its conditionally loaded based on selectionIndex. all the other components are not loaded at once.
P.S.: if you ever feel like which will get load first, just put a console log in that component and debug easily
conditionally load demo link - if you open this, the components are being loaded initially based on selectionIndex value being "one".
I'm not going to go into too much technical detail, because I feel like #hackape already provided you with a great answer as to why, point of my answer is just to explain how (to check it)
In general, I'd recommend you to download download the React Developer Tools
chrome link
firefox link
and then you can check which components are being rendered if you open the components tab inside your developer console. Here's a sandboxed example, best way to find out is to test it yourself afterall :-)
As you can see in the developer tools (bottom right), only the currently set element is being rendered

Svelte - usage of Context API (setContext/getContext) over regular props passing

Here is a simple example:
<script>
import Button from './Button.svelte';
let text = 'Click me!';
let sayHello = () => alert('Hello!');
</script>
<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
And if I get it right, since there can be lots of <Button {text} {sayHello}/>, it'll be nice to omit props passing somehow
And here comes Context API:
<script>
import Button from './Button.svelte';
import { setContext } from 'svelte';
import { text, sayHello } from './data.js';
setContext(text, 'Click me!');
setContext(sayHello, () => alert('Hello!'));
</script>
<Button/>
<Button/>
<Button/>
And somewhere in ./Button.svelte there are getContext() usage, etc
So, is the ability to omit similar props passing is the only reason to use Svelte's Context API?
So, is the ability to omit similar props passing is the only reason to use Svelte's Context API?
No, and, in my opinion, it is even not a very good usage of context.
The problem here is that you're obfuscating the data relationship between your parent component and its button children.
With props, it is explicit what data is needed by the button and where it comes from. On the other hand, with context, you only see one side of the relationship at once. In the parent, you don't see how the data is used (or even if it is still used at all). Same in the child, you don't see where it comes from.
Also, mistyping a prop, or removing one that is still needed for example, will result in an instantly visible dev warning (replete with the exact location of the problem). With context, you might end up with an undefined value that will produce weird runtime behaviour but will be hard to track down.
So, while saving a little bit of typing might seem like a good idea when you're in the process of coding and have everything fresh in your head, it actually increases the complexity of your code and might play tricks on you and give you a big headache later down the road... Not a good trade off if you want my opinion.
There are situations, however, where props are not an option. That is, when the data consumer component is not a direct child of the data provider component.
For example, you might have some kind of user session in your app. It will most likely be stored in a component near the root of your components tree (say, App), but it will be needed in components several levels of nesting deeper. For example, in a component displaying the user's name. Or somewhere else in a page, displaying some parts based on whether the user is authenticated or not.
You could pass by props through every components down the line, but this is kind of insane. This would tie all the intermediate components to data they're absolutely not concerned with.
So, in a case like this, context makes full sense. You would setContext in the App component, and can access it from just the components that need it.
Another example would be some kind of "composite" component, where you have a wrapping component (for example a form) and multiple components that can be used inside of it (for example inputs) and that depends on some settings in the container.
<Form>
<Input />
</Form>
Here, the Form component can't pass props to the Input component because the Input is not created directly in the Form component. It is fed to it by mean of a slot, and the Form can't see the content of this slot.
Still, Input is nested under Form in the resulting component tree, and so you can pass data between them through context.
To sum it up, context is really meant for situations where you can't use props. Either because it would be impracticable and lead to bad architecture, or because it is technically impossible (slots).
As an alternative to context, you could store the data in a dedicated JS module that both the provider and the consumer would access (e.g. import { setData, getData } from './data-source.js') BUT that would make your components singletons. This data could only be global. With context, on the other hand, you could have as many isolated data "scopes" as you need, one for each instance of the data provider component. In the Form example above, multiple <Form> components could coexist in your app at the same time, each having their own data in context. (They could even be nested inside each other and it would work.)
To conclude, here's a piece of advice. Context in Svelte is implemented with JS Map object, so you don't have to use raw strings as context keys. I generally use plain objects (or Symbol if you want to be fancy) that I export from something like a constants.js module. This largely mitigates the mistyping and IDE confusion issues I mentioned earlier.
constants.js
export const key = {name: 'my-context'}
Form.svelte
<script>
import { setContext } from 'svelte'
import { key } from './constants.js'
setContext(key, { ... })
</script>
<slot />
Input.svelte
<script>
import { getContext } from 'svelte'
import { key } from './constants.js'
const { ... } = getContext(key)
</script>
...
This eliminates any risk of context key collision you could have with a raw string. It turns mistyping back into a fail fast and crash noisily error (which is good). And it gives your IDE a far better clue as to what is happening in your code (an ES import can easily be parsed by dev tools, while strings are just random blobs to them), allowing it be far more helpful to you when you'll need to refactor that...

React and Redux: Are there any performance issues/possible side effects with passing a parent component as a prop to its children

I'm reviewing some code in a React-Redux codebase, and there are quite a few cases where a parent smart component is being passed as a prop to a child component:
import React from 'react';
import Child from '../components/Child';
export default class Parent extends React.Component {
constructor(props) {
super(props);
}
//...
render() {
return <Child parent={this}/>;
}
}
At initial glance, it appears that the intention here is to expose the props/state/methods of the parent to the child component. This sort of goes against many of the design patterns I've used in the past with React, but I'm not sure if it's something that is worth bringing up in a code review (it's already deployed to QA). It technically works (the child is able to call this.props.parent[method], etc) and significantly reduces the lines of code otherwise required if you pass individual handlers/slices of props/(local)state to the child. I know there are downsides (in one case, the parent property shared the same name as a reducer, so in the child component, it is unclear if this.props['reducerName'] is referring to a React Component or a mapped slice of state), but I can't be sure that the downsides are anything more than surface level.
Does something like this run the risk of unnecessary rerenders/diff checks in the child component? Does React ever need to recursively serialize components, and thus incur a lot of overhead because of circular references? Anything I can point out besides I don't think it looks right?
There are a few things I can think of why this might not be a good Idea:
This creates a very tight coupling between the parent and the component. Further, we should try to provide the minimum amount of data to the abstract modules. We call it Principle of least privilege. Here, a lot of information is being passed to the child component which will not be used and can even be abused using a lot of ways.
One case where it can be very bad idea is when the child component changes something on the date object: eg :
render() {
return (
<React.Fragment>
<ChildOne parent={this}/>;
<ChildTwo parent={this}/>;
</React.Fragment>
)
}
Now the ChildOne component can do something like :
this.props.parent.clickHandler = this.anotherHandler
This will break ChildTwo functionality.

Preventing react-redux from re-rendering whole page when state changes

I am reading several articles about how to prevent react-redux from re-rendering the whole page, when only one little thing changes.
One article suggests that instead of wrapping all into one big container (as in figure 1 here) wrapping all into smaller containers (as in figure 2 here). If something changes in Container 2, only Component 2 and Component 3 are getting re-rendered. Component 1 would not re-render.
Figure1
Figure2
I have following questions:
If I wrap everything in smaller containers, I would need "several" global states, for each container one (as indicated with the pseudo-code on the bottom of the figure). Is that common practice?
If it is ok to have "several" global states and I would need in some property from Container1 in Container2, I would need to connect that with two global states. To me that feels like it could get messy very quick. Where does what come from?
When and where would I use the react method shouldComponentUpdate()? Using the Big Container approach how would I differ which Component should be rerendered?! If implemented in the Components, they would not be "dump" anymore, because they need to access the global state in order to decide whether to re-render or not. I would not be able to reuse Components because every Component has its own special case when to rerender and when not. I am not sure where and when to use shouldComponentUpdate()
Please note that I am pretty new to this and might have made wrong assumptions etc. I basically want to know how not to re-render the whole page, when only one thing needs to be updated. The results from asking google differ a lot.
Your second approach is the way to go, though your definition of a global state is a bit misleading.
Basically, you want to have exactly one "global state". This is what is referred to as "store". All components that need to receive parts of the store are connected to it using react-redux' connect function.
Now, connect(...) is actually a HOC which wraps your component and passes only defined parts of the store to it. This way, the component (and its' children) only re-render when its' defined props change.
Don't be afraid to use connect() more often. You just have to be careful what parts of the store you pass to the container and this is exactly where performance can become an issue.
This should answer your first question. The second one is a question of design. Design in terms of how your app and maybe also in terms of how your datasource is structured. As said before, you want to have a minimum of props passed to a component so it doesn't re-render when other parts of the store change.
For the third question, you first have to understand that 'dumb components' can, of course, receive props from their parent components/containers. Dumb just means that they don't get to decide whether a re-render should happen or not. Dumb components are there to present/display data and that's it.
Let's say you have a really simple store:
const store = {
posts: {
all: [],
isFetching: false,
err: {},
}
}
And you connect your container to it like this:
function mapStateToProps(store) {
return {
posts: store.posts.all,
isFetching: store.posts.isFetching,
err: store.posts.err,
};
}
#connect(mapStateToProps)
And this container has three dumb components it can use:
A posts component, which receives all posts and displays them using another dumb child (pseudoCode, you get the point):
function posts = (posts) => {
posts.map((post, id) => (
<otherDumbComponent post={post} key={id} />
));
}
One to display just a spinner while isFetching
One to display the error if there's one.
Now, if only isFetching has changed, only the second component will re-render and that's it. Oh, and shouldComponentUpdate() is something you probably don't want to use, because, well.. there are many good blog posts about it.

Is connect() in leaf-like components a sign of anti-pattern in react+redux?

Currently working on a react + redux project.
I'm also using normalizr to handle the data structure and reselect to gather the right data for the app components.
All seems to be working well.
I find myself in a situation where a leaf-like component needs data from the store, and thus I need to connect() the component to implement it.
As a simplified example, imagine the app is a book editing system with multiple users gathering feedback.
Book
Chapters
Chapter
Comments
Comments
Comments
At different levels of the app, users may contribute to the content and/or provide comments.
Consider I'm rendering a Chapter, it has content (and an author), and comments (each with their own content and author).
Currently I would connect() and reselect the chapter content based on the ID.
Because the database is normalised with normalizr, I'm really only getting the basic content fields of the chapter, and the user ID of the author.
To render the comments, I would use a connected component that can reselect the comments linked to the chapter, then render each comment component individually.
Again, because the database is normalised with normalizr, I really only get the basic content and the user ID of the comment author.
Now, to render something as simple as an author badge, I need to use another connected component to fetch the user details from the user ID I have (both when rendering the chapter author and for each individual comment author).
The component would be something simple like this:
#connect(
createSelector(
(state) => state.entities.get('users'),
(state,props) => props.id,
(users,id) => ( { user:users.get(id)})
)
)
class User extends Component {
render() {
const { user } = this.props
if (!user)
return null
return <div className='user'>
<Avatar name={`${user.first_name} ${user.last_name}`} size={24} round={true} />
</div>
}
}
User.propTypes = {
id : PropTypes.string.isRequired
}
export default User
And it seemingly works fine.
I've tried to do the opposite and de-normalise the data back at a higher level so that for example chapter data would embed the user data directly, rather than just the user ID, and pass it on directly to User – but that only seemed to just make really complicated selectors, and because my data is immutable, it just re-creates objects every time.
So, my question is, is having leaf-like component (like User above) connect() to the store to render a sign of anti-pattern?
Am I doing the right thing, or looking at this the wrong way?
I think your intuition is correct. Nothing wrong with connecting components at any level (including leaf nodes), as long as the API makes sense -- that is, given some props you can reason about the output of the component.
The notion of smart vs dumb components is a bit outdated. Rather, it is better to think about connected vs unconnected components. When considering whether you create a connected vs unconnected components, there are a few things to consider.
Module boundaries
If you divided your app into smaller modules, it is usually better to constrain their interactions to a small API surface. For example, say that users and comments are in separate modules, then I would say it makes more sense for <Comment> component to use a connected <User id={comment.userId}/> component rather than having it grab the user data out itself.
Single Responsibility Principle
A connected component that has too much responsibility is a code smell. For example, the <Comment> component's responsibility can be to grab comment data, and render it, and handle user interaction (with the comment) in the form of action dispatches. If it needs to handle grabbing user data, and handling interactions with user module, then it is doing too much. It is better to delegate related responsibilities to another connected component.
This is also known as the "fat-controller" problem.
Performance
By having a big connected component at the top that passes data down, it actually negatively impacts performance. This is because each state change will update the top-level reference, then each component will get re-rendered, and React will need to perform reconciliation for all the components.
Redux optimizes connected components by assuming they are pure (i.e. if prop references are the same, then skip re-render). If you connect the leaf nodes, then a change in state will only re-render affected leaf nodes -- skipping a lot of reconciliation. This can be seen in action here: https://github.com/mweststrate/redux-todomvc/blob/master/components/TodoItem.js
Reuse and testability
The last thing I want to mention is reuse and testing. A connected component is not reusable if you need to 1) connect it to another part of the state atom, 2) pass in the data directly (e.g. I already have user data, so I just want a pure render). In the same token, connected components are harder to test because you need to setup their environment first before you can render them (e.g. create store, pass store to <Provider>, etc.).
This problem can be mitigated by exporting both connected and unconnected components in places where they make sense.
export const Comment = ({ comment }) => (
<p>
<User id={comment.userId}/>
{ comment.text }
</p>
)
export default connect((state, props) => ({
comment: state.comments[props.id]
}))(Comment)
// later on...
import Comment, { Comment as Unconnected } from './comment'
I agree with #Kevin He's answer that it's not really an anti-pattern, but there are usually better approaches that make your data flow easier to trace.
To accomplish what you're going for without connecting your leaf-like components, you can adjust your selectors to fetch more complete sets of data. For instance, for your <Chapter/> container component, you could use the following:
export const createChapterDataSelector = () => {
const chapterCommentsSelector = createSelector(
(state) => state.entities.get('comments'),
(state, props) => props.id,
(comments, chapterId) => comments.filter((comment) => comment.get('chapterID') === chapterId)
)
return createSelector(
(state, props) => state.entities.getIn(['chapters', props.id]),
(state) => state.entities.get('users'),
chapterCommentsSelector,
(chapter, users, chapterComments) => I.Map({
title: chapter.get('title'),
content: chapter.get('content')
author: users.get(chapter.get('author')),
comments: chapterComments.map((comment) => I.Map({
content: comment.get('content')
author: users.get(comment.get('author'))
}))
})
)
}
This example uses a function that returns a selector specifically for a given Chapter ID so that each <Chapter /> component gets its own memoized selector, in case you have more than one. (Multiple different <Chapter /> components sharing the same selector would wreck the memoization). I've also split chapterCommentsSelector into a separate reselect selector so that it will be memoized, because it transforms (filters, in this case) the data from the state.
In your <Chapter /> component, you can call createChapterDataSelector(), which will give you a selector that provides an Immutable Map containing all of the data you'll need for that <Chapter /> and all of its descendants. Then you can simply pass the props down normally.
Two major benefits of passing props the normal React way are traceable data flow and component reusability. A <Comment /> component that gets passed 'content', 'authorName', and 'authorAvatar' props to render is easy to understand and use. You can use that anywhere in your app that you want to display a comment. Imagine that your app shows a preview of a comment as it's being written. With a "dumb" component, this is trivial. But if your component requires a matching entity in your Redux store, that's a problem because that comment may not exist in the store yet if it's still being written.
However, there may come a time when it makes more sense to connect() components farther down the line. One strong case for this would be if you find that you're passing a ton of props through middle-man components that don't need them, just to get them to their final destination.
From the Redux docs:
Try to keep your presentation components separate. Create container
components by connecting them when it’s convenient. Whenever you feel
like you’re duplicating code in parent components to provide data for
same kinds of children, time to extract a container. Generally as soon
as you feel a parent knows too much about “personal” data or actions
of its children, time to extract a container. In general, try to find
a balance between understandable data flow and areas of responsibility
with your components.
The recommended approach seems to be to start with fewer connected container components, and then only extract more containers when you need to.
Redux suggests that you only connect your upper-level containers to the store. You can pass every props you want for leaves from containers. In this way, it is more easier to trace the data flow.
This is just a personal preference thing, there is nothing wrong to connect leaf-like component to the store, it just adds some complexity to your data flow, thus increase the difficulty to debug.
If you find out that in your app, it is much easier to connect a leaf-like component to the store, then I suggest do it. But it shouldn't happen very often.

Categories

Resources