Pass method from one file to another in react - javascript

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

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'))

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

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.

How can a parent component communicate with a child component in Vue.js?

This is what I have:
<div id='vnav-container'>
<input type="text" v-model="searchTerm" v-on:keyup="search" class="vnav-input">
<menu :items="menu"></menu>
</div>
The outer component contains a search-input and a menu component.
When the user performs a search on the outer component, I need to call a method on the menu component, or emit an event, or whatever, as long as I can communicate to the menu component saying it should filter itself based on the new criteria.
I've read somewhere that calling methods on child components is discouraged and that I should use events. I'm looking at the docs right now, but I can only see an example of a child talking to a parent, not the other way around.
How can I communicate to the menu component as the search criteria changes?
EDIT
According to some blog posts, there used to be a $broadcast method intended to talk to child components but the documentation about that just vanished. This used to be the URL: http://vuejs.org/api/#vm-broadcast
The convention is "props down, events up". Data flows from parents to child components via props, so you could add a prop to the menu, maybe:
<menu :items="menu" :searchTerm="searchTerm"></menu>
The filtering system (I'm guessing it's a computed?) would be based on searchTerm, and would update whenever it changed.
When a system of components becomes large, passing the data through many layers of components can be cumbersome, and some sort of central store is generally used.
Yes, $broadcast was deprecated in 2.x. See the Migration guide for some ideas on replacing the functionality (which includes event hubs or Vuex).
Or you can create the kind of simple store for that.
First off, let's create the new file called searchStore.js it would just VanillaJS Object
export default {
searchStore: {
searchTerm: ''
}
}
And then in files where you are using this store you have to import it
import Store from '../storedir/searchStore'
And then in your component, where you want to filter data, you should, create new data object
data() {
return {
shared: Store.searchStore
}
}
About methods - you could put method in your store, like this
doFilter(param) {
// Do some logic here
}
And then again in your component, you can call it like this
methods: {
search() {
Store.doFilter(param)
}
}
And you are right $broadcast and $dispatch are deprecated in VueJS 2.0

ReactJs: change state in response to state change

I've got a React component with an input, and an optional "advanced input":
[ basic ]
Hide Advanced...
[ advanced ]
The advanced on the bottom goes away if you click "Hide Advanced", which changes to "Show Advanced". That's straightforward and working fine, there's a showAdvanced key in the state that controls the text and whether the advanced input is rendered.
External JS code, however, might change the value of advanced, in which case I want to show the [advanced] input if it's currently hidden and the value is different than the default. The user should be able to click "Hide Advanced" to close it again, however.
So, someone external calls cmp.setState({advanced: "20"}), and I want to then show advanced; The most straightforward thing to do would just be to update showAdvanced in my state. However, there doesn't seem to be a way to update some state in response to other state changes in React. I can think of a number of workarounds with slightly different behavior, but I really want to have this specific behavior.
Should I move showAdvanced to props, would that make sense? Can you change props in response to state changes? Thanks.
Okay first up, you mention that a third party outside of your component might call cmp.setState()? This is a huge react no-no. A component should only ever call it's own setState function - nothing outside should access it.
Also another thing to remember is that if you're trying change state again in response to a state change - that means you're doing something wrong.
When you build things in this way it makes your problem much harder than it needs to be. The reason being that if you accept that nothing external can set the state of your component - then basically the only option you have is to allow external things to update your component's props - and then react to them inside your component. This simplifies the problem.
So for example you should look at having whatever external things that used to be calling cmp.setState() instead call React.renderComponent on your component again, giving a new prop or prop value, such as showAdvanced set to true. Your component can then react to this in componentWillReceiveProps and set it's state accordingly. Here's an example bit of code:
var MyComponent = React.createClass({
getInitialState: function() {
return {
showAdvanced: this.props.showAdvanced || false
}
},
componentWillReceiveProps: function(nextProps) {
if (typeof nextProps.showAdvanced === 'boolean') {
this.setState({
showAdvanced: nextProps.showAdvanced
})
}
},
toggleAdvancedClickHandler: function(e) {
this.setState({
showAdvanced: !this.state.showAdvanced
})
},
render: function() {
return (
<div>
<div>Basic stuff</div>
<div>
<button onClick={this.toggleAdvancedClickHandler}>
{(this.state.showAdvanced ? 'Hide' : 'Show') + ' Advanced'}
</button>
</div>
<div style={{display: this.state.showAdvanced ? 'block' : 'none'}}>
Advanced Stuff
</div>
</div>
);
}
});
So the first time you call React.renderComponent(MyComponent({}), elem) the component will mount and the advanced div will be hidden. If you click on the button inside the component, it will toggle and show. If you need to force the component to show the advanced div from outside the component simply call render again like so: React.renderComponent(MyComponent({showAdvanced: true}), elem) and it will show it, regardless of internal state. Likewise if you wanted to hide it from outside, simply call it with showAdvanced: false.
Added bonus to the above code example is that calling setState inside of componentWillReceiveProps does not cause another render cycle, as it catches and changes the state BEFORE render is called. Have a look at the docs here for more info: http://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops
Don't forget that calling renderComponent again on an already mounted component doesn't mount it again, it just tells react to update the component's props and react will then make the changes, run the lifecycle and render functions of the component and do it's dom diffing magic.
Revised answer in comment below.
My initial wrong answer:
The lifecycle function componentWillUpdate will be ran when new state or props are received. You can find documentation on it here: http://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate
If, when the external setState is called, you then set showAdvanced to true in componentWillUpdate, you should get the desired result.
EDIT: Another option would be to have the external call to setState include showAdvanced: true in its new state.

Categories

Resources