Will JSX conditional rendering out of an object code split? - javascript

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

Related

When is it necessary to use `rerender` with the React Testing Library?

In times past, my colleagues and I would typically write React Testing Library (RTL) tests for the main parent components, which often have many nested child components. That testing made sense and worked well. Btw the child components in question are very much dedicated to that parent component and not of the reusable variety.
But now we're trying to write RTL tests for every single component. Today I was trying to build tests for an Alerts component, which is the parent of an Alert component and about 4 levels down from the top-level component. Here's some sample code in my test file:
function renderDom(component, store) {
return {
...render(<Provider store={store}>{component}</Provider>),
store,
};
}
let store = configureStore(_initialState);
const spy = jest.spyOn(store, 'dispatch');
const { queryByTestId, queryByText, debug } = renderDom(
<Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} />,
store
);
I then started writing the typical RTL code to get the Alerts component to do its thing. One of these was to click on a button which would trigger an ADD_ALERT action. I stepped through all of the code and the Redux reducer was apparently working correctly with a new alert, as I intended, yet back in the Alerts component, question.alerts remained null whereas in the production code it was definitely being updated properly with a new alert.
I spoke with a colleague and he said that for this type of test, I would need to artificially rerender the component like this:
rerender(<Provider store={store}><Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} /></Provider>);
I tried this and it appears to be a solution. I don't fully understand why I have to do this and thought I'd reach out to the community to see if there was a way I could avoid using rerender.
It's hard to be certain without seeing more of your code, but my typical approach with RTL is to take the fireEvent call that simulates clicking the button and wrap it in an act call. This should cause React to finish processing any events from your event, update states, rerender, etc.
Alternatively, if you know that a particular DOM change should occur as a result of firing the event, you can use waitFor. An example from the React Testing Library intro:
render(<Fetch url="/greeting" />)
fireEvent.click(screen.getByText('Load Greeting'))
await waitFor(() => screen.getByRole('alert'))

Isolating components from integrated app code in React

I'm looking for help isolating out components from my body of code. I can't figure out how to link the input and output variables that are currently integrated throughout the app code. I've tried pulling out short parts of rendered html(/jsx) and helper functions, but so far no luck. (this is a requirement for a training program.)
My approach has been to cut and paste the desired segment into a new javascript file, replace in the app function with a component call to the new js file (via exporting from component, importing in app), replace the object variable in the component with a simple abstract name (e.g., change "task" to "object") and establish equivilancies in the component call. However, I can't get this to work -- when I remove the original variables and replace with component calls, everything else stops recognizing their variables.
So I'm looking for advice and help on how to make components of the sections detailed below. Sorry if this is vague without the specific error messages and an entire detailing of my approaches so far -- I figure there is something I'm fundamentally missing about how to isolate components from the code I currently have -- All the information I've found that should be relevant relies on props to be attached to property variables in the app component, which is not how I have my app coded. I'm hoping I don't have to gut my code and start over.
To begin with, this is the smallest contained code, the basic content of the html:
//ListItem
<li
key={listItem.index}
onClick={() => {
handleClick(listItem.index);
}}
> {listItem.string}
</li>
after that would be its container element:
//Div
<div>
<h2>w/e</h2>
<ul>
{list
.map((item, index) => ({ ...item, index }))
.filter((item) => item.position === "right")
.map((listItem) => (
<ListItem props.../> // the ListItem component defined before
))}
</ul>
</div>
so that finally, i would have:
<body><Div props... /></body>
the last thing I'm trying to seperate as a component is an object array being used to set state via useState:
//Tasks
//const [list, setList] = useState(
[
{ string: "string", position: "position" } //and many more
]);
For each piece of the code that you extract, ask yourself "what information does this code need to render?" The answer to that question becomes your props. A ListItem needs key, onClick, and its contents. You can pass the contents as a string property, or you can make use of props.children.
Not every single JSX element needs to be its own component. Think about which chunks of code change depending on the variables of your app. Those variables become props and those code chunks become components.

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...

Pass method from one file to another in react

Say in my first file, open.jsx, I have:
// Default Import Statements here
var open = React.createClass({
render() {
return (
<div>
<Dialog
title="Test"
ref="openDialog">
</Dialog>
</div>
);
},
_handleTouchTap() {
this.refs.openDialog.setState({open:true});
}
});
module.exports = open;
And in my app.jsx file I have:
const testComponent = React.createClass({
render() {
return (
<FlatButton label="Test" onTouchTap={this._handleTouchTap}/>
);
},
_handleTouchTap: function() {
Open._handleTouchTap();
}
});
module.exports = testComponent;
The error I am getting currently is:
Uncaught TypeError: Open._handleTouchTap is not a function
Does anyone know how I can pass methods in between files for React?
I want to call open.jsx's _handleTouchTap() method when they press the button in app.jsx.
When you call Open._handleTouchTap, you are attempting to call the method as if it was static, on the Open class. This method, however, is only available once an Open component has been instantiated. You must attach a ref to the component and call the method via this.refs.someOpenComponent._handleTouchTap().
You may want to provide more of your code so better examples can be provided.
Also, methods with an underscore in front of their names typically denote "private" methods, and should not be called from a different class. You may want to consider renaming this function so it is more clear what its purpose is.
I'm assuming you want to render some page with a button, and show the dialog as soon as someone presses the FlatButton. I also notice you're using material-ui, so let's go with that.
When starting any React project, it's a good idea to think about your component hierarchy. Because you're using material-ui and the Dialog component's opening is controlled by passing props, it's easiest to use the following approach.
Simple case
Use a root component App (in app.jsx), which mounts a button and mounts a dialog, but the dialog is initially in a hidden state (the "open" prop on Dialog defaults to false) so doesn't visually show up yet (even though it is mounted). In this case, you will want the button to set the open prop on Dialog to true as soon as the button is pressed.
Please note I would recommend separating most of this rendering stuff into separate components; for illustration purposes, let's keep everything in App.jsx.
The way you want to organise in this case is as follows:
// App.jsx (render + handle click function only)
render: function() {
return (
<div>
<FlatButton label="Test" onTouchTap={this._handleTapTestButton}/>
<Dialog
title="Test"
open={this.state.dialogOpen}>
<div>My dialog contents</div>
</Dialog>
</div>
);
},
_handleTapTestButton: function() {
this.setState({dialogOpen: !this.state.dialogOpen}); // flip the state
}
See? No refs needed even (and that's good!). Now, this works fine if your Dialog component is located nice and close to your FlatButton.
More complex case: Dialog is far away from FlatButton
Your next question might be "how can I organise this when the Dialog component is nested somewhere deep inside a totally different component that is not a child or parent of the App.jsx component", but instead a sibling?
Well, this smells a little to me (just an opinion). It's not an anti-pattern per sé, but if you can avoid this, I would recommend you do. Ie: for your own convenience and for maintainability's sake, try to keep components that naturally interact with each other close (in terms of parent-child) to each other in the component hierarchy. This way, you can communicate pretty easily using props (see React's info on this. That's definitely not an absolute rule though, there are plenty of reasons to deviate from that.
Let's assume you have a valid case for not doing that, and even worse: the component are siblings, not direct or indirect grandparent/parent/child.
In that case, you have two options:
Use a store and associated events (or any other javascript code that communicates state) to communicate the state change to the other component (ie using Flux, Redux, or whatever you prefer). In this case, when the button is clicked, you fire an event somewhere that gets picked up by the other component. This event triggers a state change in the other component. Warning: this can get unmanageable pretty quickly, which is one of the reasons state-managing-frameworks like Flux and Redux exist.
OR, onTouchTap, have the FlatButton call a function that was passed down from a shared parent component. This function then flips the state at the shared parent component, and passes this state as props to the Dialog. Ie, when both components share a grandparent somewhere, you can define a function at the grandparent level and pass that function as a prop down to the FlatButton. The function's role is to change the state at the grandparent (dialogOpen). This state is then passed down one or more components as a prop all the way down the hierarchy until it ends up at the Dialog, which will auto show itself as the prop switches to true.
There are serious advantages/disadvantages to either approach. The first approach leaks your UI rendering logic into your stores (which is usually inevitable anyway, but can be managed using things like Flux), the second leaks it into the component hierarchy (tricky for maintainability) and tends to create tight coupling (yuck).

What's the best way to inject a React component into another React component? Or should I bother?

While working on my first large React application, I started feeling uneasy about the tight coupling of nested components, especially when writing unit tests.
If is a high-level component, and its render function returns <div><Bar><Baz /></Bar></div>, I would like to be able to test Foo, Bar, and Baz in isolation.
I searched for advice but didn't find any. So, on my own, I came up with two methods of injection: factory functions and via the outer component's properties. (I am not talking about Children. I'm talking about "baked in" dependencies--the sort people usually import via require or import statements.
Properties
const Baz = React.createClass({
render() {
return <p>Inner</p>;
}
});
const Foo = React.createClass({
render() {
const Bar = this.props.innerComponent;
return <Bar />;
}
});
ReactDOM.render(
<Foo innerComponent={Baz} />,
document.getElementById('container')
);
Factory
const Baz = React.createClass({
render() {
return <p>Inner</p>;
}
});
const fooFactory = innerComponent => {
return React.createClass({
render() {
const Bar = innerComponent;
return <Bar />;
}
});
};
const Foo = fooFactory(Baz);
ReactDOM.render(
<Foo innerComponent={Baz} />,
document.getElementById('container')
);
Or am I just taking decoupling too far? I see almost no one else doing this in any tutorials or examples.
Would you be inclined to inject components sometimes but not other times? In what circumstances? And, if you did it, would you use one of the above techniques or do it some other way?
This is a pattern you'll see from time to time when there is a run-time necessity for specifying a component type which the component it's passed to will instantiate itself; for example, the React TransitionGroup component takes such a property called component:
By default ReactTransitionGroup renders as a span. You can change this behavior by providing a component prop. For example, here's how you would render a <ul>:
<ReactTransitionGroup component="ul">
...
</ReactTransitionGroup>
Every DOM component that React can render is available for use. However, component does not need to be a DOM component. It can be any React component you want; even ones you've written yourself! Just write component={List} and your component will receive this.props.children.
It's less common to use this pattern for the user-specified contents of a component, since this is solved more elegantly by this.props.children.
However, as far as testing is concerned, the common solution is simply to export each component separately and test it in isolation. If a high-level component composes them in a certain way, all you really need to test is that they are composed properly, since the components are also tested individually.
There is a common pattern of separating "smart" and "dumb" components; dumb components simply render what they're given, and are very easily unit tested. Smart components may fetch data (e.g. via Ajax, or from a flux store), compose specific dumb components, or make other "decisions," and can be tougher to test—but again, since the dumb components are already tested, ideally you can just check that the smart component behaves correctly and renders the right thing.
To use your example, it's Foo's job to render <div><Bar><Baz /></Bar></div>; it's a smart component, and the test should ensure that it renders exactly that. Bar's job, however, is to render its children, and is more easily tested.
You also have the ability to mock out a component with TestUtils.mockComponent.
Check out mochajs. This testing framework overrides import / require, which solves your problem.
It does mean that your subcomponents need to be written as ECMA 6 or NodeJS modules, though - but that really just means they need to be in separate source files.

Categories

Resources