I want to destroy the root Preact DOM node. I initially render my component as follows:
import { h, render } from 'preact';
import App from "./components/App";
render(<App />, document.querySelector("#app");
How do I destroy App? Do I simply unmount the #app DOM node, or does Preact offer a method similar to React's unmountComponentAtNode() method?
Disclaimer: I work on preact.
Any rendered preact app can be easily destroyed by passing null to render:
render(null, document.querySelector("#app"));
We don't need any special functions to do that and have chosen to keep a small API surface area. The implementation for unmountComponentAtNode in preact-compat does literally just call render with null:
// Excerpt from compat for Preact X
function unmountComponentAtNode(container) {
if (container._prevVNode!=null) {
render(null, container);
return true;
}
return false;
}
There is no method like unmountComponentAtNode() without preact-compat.
A workaround is to use the third argument of the render() method to replace the component you want to unnmount by '' or null like suggested here :
https://github.com/developit/preact/issues/53#issuecomment-184868295
Related
I'm rendering a react component like this
export class MyClass {
render(state) {
ReactDOM.render(<Dashboard {...state} />, document.getElementById("root"));
}
}
The render function is called externally by the environment my class is injected in. Although this works fine, the problem is that the environment can call that render method again with an other state object. How can I pass that data to that rendered Dashboard component?
I tried to run ReactDOM.render again, but then nothing happens. The only solution I found was to remove that root element, insert it again and then run ReactDOM.render again, but that doesn't really sound like a solid solution to this trivial problem. Any suggestions how to do this?
I am trying to declare two different React elements that I would like to render. The both elements are separated elements such as displayed elements (App.jsx) and the customized account system (Login.jsx). But in my test I have the same code in the both jsx file to ensure that the issue is not related to a specific part of them.
I have also created an /imports/startup/client/index.js file (called in the /client/main.js file):
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import './accounts-config.js';
import App from '/imports/ui/App.jsx';
import Login from '/imports/ui/Login.jsx';
Meteor.startup(() => {
render(<App />, document.getElementById('app'));
render(<Login />, document.getElementById('login'));
})
and the /client/main.html contains the related div tags:
...
<div id="app"></div>
<div id="login"></div>
...
The issue is that the second render is never displayed (here, the div login) and I observe that only the first render is interpreted.
All the examples that I've found only deals with a single react element. So I wonder how to use several separated react elements like it is in my html file ?
I am newbie in the meteorjs and react world , so maybe I didn't get the right philosophy...
You can make use of React 16 new feature that is portal.
For how to use ReactDOM.createPortal please refer to following link:
How to use ReactDOM.createPortal() in React 16?
I have solved my issue using only one render in the Meteor.startup(() (in my index.js).
The React doc specifies that only one render can be declared in the Meteor.startup(() (in my index.js).
https://reactjs.org/docs/components-and-props.html
My code is the following:
in my index.js
Meteor.startup(() => {
render(<App />, document.getElementById('app'));
})
The trick is that this Super component (App.jsx) has to be used to call all the other components. In my example by calling the Login component:
render() {
return (
<div className="container">
<Login />
</div>
)
}
I believe because the render() statement has an implicit return statement as well, so since it can only execute and return one, the next render statement isn't executed.
I just started out trying mobx-react using stores, and want to use a store plus a single observable, but can't even get this to work.
With #observer, I get the error Uncaught TypeError: Cannot assign to read only property 'render' of object '#<ProxyComponent>'.
Without it, the value becomes 1.
I'm not sure what's going wrong here, any ideas?
import {observable} from 'mobx'
import {inject, observer} from 'mobx-react'
class AppStore {
#observable value = 1;
}
#inject('store') #observer
class App extends React.Component {
render(){
const {store} = this.props
return (
<div>
{store.value}
</div>
)
}
}
const render = (Component) => {
const appStore = new AppStore()
ReactDOM.render(
<AppContainer>
<Provider store={appStore}>
<Component/>
</Provider>
</AppContainer>,
document.getElementById('root'),
)
}
render(App)
Your code is bit unclear, like from where are you importing Provider and ReactDOM.And also since render is a function used by ReactDOM so the render() function you have defined might conflict with the built in render() function.
And also here it is explained
In general there are three ways in which you can pass stores in MobX
1) Explicitly via props. Easy to test and clear to follow, but can become
clumpsy when you have deeply nested structures or many stores (you can
solve the latter by having a store for stores)
2) Import stores in the
components directly and just use them :) It's the MVP of passing
stores around, but stand alone testing of components becomes tricky
quickly as you have to make sure your global stores are in the right
state first
3) Pass stores around via React's context mechanism. Redux's
Provider uses that, as does the mobx-connect package. Context is
passed implicitly and deep component can extract data out of the
context, but it is still easy to test as you only have to make sure
you set up some context before testing the component.
In your case you are using the 3rd point.So I have created a jsfiddle here,
where the store is passed as props as in point 1.
Turns out this was a configuration (webpack hot loader) issue, and not an issue of the code itself (which runs under a jsfiddle).
Adding 'react-hot-loader/babel' to 'plugins' in webpack's 'babel-loader' appears to work.
What is alternative way of using package 'react-meteor-data' ? I'm using ES6 for writing react component. What would be best approach for writing meteor subscription in react component so that it will re-render the component if anything change on the server side.
What would be best approach for writing meteor subscription in react
component so that it will re-render the component if anything change
on the server side.
The best approach is using react-meteor-data. What is wrong with this package, that makes you think to not use it?
It even allows you to separate / decouple React Components from Meteor. This is really great because when you reach the point of having written some components that you want to reuse in another non-Meteor project you are free to go without greater hassle.
Of course you could write your own subscription container for react, but with this package you have all important stuff you need plus it is maintained and tested.
If you have trouble setting up a container for subscriptions, you may dig deeper into these tutorials:
https://guide.meteor.com/react.html
https://themeteorchef.com/tutorials/using-create-container
Using react-meteor-data, you first create a HOC container that subscribes to the data and passes it down as props to your component.
Container:
import { createContainer } from 'meteor/react-meteor-data';
export default FooContainer = createContainer(() => {
// Do all your reactive data access in this method.
// Note that this subscription will get cleaned up when your component is unmounted
var handle = Meteor.subscribe("todoList", this.props.id);
return {
currentUser: Meteor.user(),
listLoading: ! handle.ready(),
tasks: Tasks.find({listId: this.props.id}).fetch(),
};
}, Foo);
Component:
import FooContainer from './FooContainer';
class App extends React.Component {
render() {
return (
<div>
{this.props.currentUser.name}
</div>
)
}
}
export default FooContainer(App);
Check out React docs for Higher Order Components to understand how the container works.
Is there a way to unmount and garbage collect a React Component that was mounted using TestUtils.renderIntoDocument inside a jsdom test?
I'm trying to test something that happens on componentWillUnmount and TestUtils.renderIntoDocument doesn't return any DOM node to call React. unmountComponentAtNode on.
No, but you can simply use ReactDOM.render manually:
var container = document.createElement('div');
ReactDOM.render(<Component />, container);
// ...
ReactDOM.unmountComponentAtNode(container);
This is exactly what ReactTestUtils does anyway, so there's no reason not to do it this way if you need a reference to the container.
Calling componentWillUnmount directly won't work for any children that need to clean up things on unmount. And you also don't really need to replicate the renderIntoDocument method, either since you can just use parentNode:
React.unmountComponentAtNode(React.findDOMNode(component).parentNode);
Update: as of React 15 you need to use ReactDOM to achieve the same:
import ReactDOM from 'react-dom';
// ...
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(component).parentNode);
Just to update #SophieAlpert answer. React.renderComponent will be deprecated soon so you should use ReactDOM methods instead:
var container = document.createElement('div');
ReactDOM.render(<Component />, container);
// ...
ReactDOM.unmountComponentAtNode(container);
After your test you can call componentWillUnmount() on the component manually.
beforeEach ->
#myComponent = React.addons.TestUtils.renderIntoDocument <MyComponent/>
afterEach ->
#myComponent.componentWillUnmount()
Just stumbled across this question, figure I would provide a way to directly tackle it using the described renderIntoDocument API. This solution works in the context of PhantomJS.
To mount onto the document node:
theComponent = TestUtils.renderIntoDocument(<MyComponent/>);
To unmount from the document node:
React.unmountComponentAtNode(document);