Invariant Violation on changing route - react-router

I'm using a file upload component, here's an extract of the relevant part:
class FileUpload extends React.Component {
static propTypes = {
edit_mode: React.PropTypes.bool
};
constructor(props) {
super(props);
this.delete_asset = this.delete_asset.bind(this);
}
delete_asset(id, event) {
}
render() {
return <div>
{this.props.edit_mode ?
<button onClick={() => this.delete_asset()}>Delete</button>
: <span />
}
</div>;
}
}
This component is wired up within a react-router setup, and on a route change, it's one of several nested componentes of the route target.
Whenever I change the route, I get the following exception, even if edit_mode does not change on the route change:
invariant.js:39 Uncaught Invariant Violation: findComponentRoot(..., >0.1.1.1.$0.1.1.0): Unable to find element. This probably means the DOM was unexpectedly mutated (e.g., by the browser), usually due to forgetting a when using tables, nesting tags like , , or , or using non-SVG elements in an parent. Try inspecting the child nodes of the element with React ID ``.
The exception is not thrown then I omit the onClick handler.
Any ideas?

Related

Is there any better way to pass function to props with LitElement?

I'm using polymer LitElement and i tried to pass a function to props but did'nt work, this is the way i found to work, but its awfull... Any better suggestions?
import { LitElement, html, customElement, property } from 'lit-element';
#customElement('my-element')
export class MyElement extends LitElement {
onButtonClick = function name (){
console.log("Clicked!!")
}
render() {
return html`
<c-button text="Button Name" onClick="${this.onButtonClick}" />
`;
}
}
#customElement("c-button")
export class CButton extends LitElement{
#property() text;
#property() onClick;
handleClick(){
let fct = eval(`( ${this.onClick} )` )
fct();
}
render(){
return html`
<button #click="${this.handleClick}" > ${this.text} </button>
`
}
}
By default lit-html data bindings set an attribute which means it'll have to convert the value to a string.
Instead of onClick="${this.onButtonClick}" prefix it with . like this .onClick="${this.onButtonClick}". That will set a property in JavaScript instead and the method will be passed by reference.
#abraham's answer is good for the general case: you can set properties, including functions, with the . syntax.
However, if you're specifically dealing with events, then I would use event bindings (# syntax) and make sure the the event you're interested in is either bubbling and composed (as click is) so that it'll propagate out of the child component, or re-dispatched by the child component. Events are a good model, I'd use them for event-like things.

How should I parse this json object in react with lifecycle method?

How should I parse this using lifecycle methods?
{"blocks":[{
"key":"33du7",
"text":"Hello there!",
"type":"unstyled",
"depth":0,
"inlineStyleRanges":[],
"entityRanges":[],
"data":{}}],
"entityMap":{}
}
I want to render the text in my component but I don't know why it throws undefined error. How should I call it?
This is my component:
class Blog extends Component{
constructor(props){
super(props);
this.blogContent = props.blogContent;
this.blogId = props.blogId;
this.handleRemoveBlog = this.handleRemoveBlog.bind(this);
this.state = {
blog__: '',
};
}
handleRemoveBlog(blogId){
this.props.removeBlog(blogId);
}
This is my lifecycle method , I would use this.setState but first of all it's giving undefined in console.
componentWillMount(){
this.state.blog__ = JSON.parse(this.blogContent);
console.log(this.state.blog__.text); // this gives undefined
}
This is the render part..
The data is coming from Firebase.
And {this.blogcontent} gives that json string that I previously mentioned.
render(props) {
return(
<div className = "blog header">
<p>{this.blog__.text}</p>
</div>
);
}
}
Blog.proptypes = {
blogContent: Proptypes.string
}
This would mostly depend on where you are getting this object from. If it is fetched over the network then the best place to pass it is in the componentDidMount. The reason for this is that the alternative lifecyle method (componentWillMount) does not guarantee a re-render of your component since it does not wait for async actions to finish execution before passing control down to your render method. Hence componentDidMount is best because as soon as new props are received or state is changed it will trigger a re-render. However, if this object is pulled from within the application then chances are, it will work just fine even if pulled within componentWillMount. This is because that operation would be much quicker, so much that control would be passed down to the render method with the new props. This is not guaranteed especially if you want to set state in the process (setting state is also async, so control might execute the rest of the code before all the required data is received).
In short, pass this to componentDidMount and in your render function, before accessing this prop, make sure that it exists. That is, instead of
render() {
return <div>{this.props.theObject.blocks[0].key}</div>
}
rather do:
render() {
return <div>{this.props.theObject && this.props.theObject.blocks[0].key}</div>
}
This is how you would do it (assuming you are getting the file over the network using axios)
componentDidMount() {
axios.get('url/to/the/file')
.then(fileData => this.setState({
data: fileData
});
}
render() {
// return whatever you want here and setting the inner html to what the state holds
}
You should not modify the state using
this.state.blog__ = JSON.parse(this.blogContent);
The proper way to do it is using the this.setState() method:
this.setState({blog__: JSON.parse(this.blogContent)})
Then, to ensure that the component will be re-rendered, use the method shouldComponentUpdate():
shouldComponentUpdate(nextProps,nextState) {
if(nextState != this.state) {
this.forceUpdate()
}
}
Take a look at the State and Lifecycle docs.
Other point: Use componentDidMount() instead of componentWillMount(), because it will get deprecated in the future.
Atention: The setState() is an asynchronous method. So, it won't instant update your state.
Use this.setState({}) in your componentWillMount function instead assign the data to the variable. Also I recommend to use componentDidMount instead of componentWillMount because it's getting deprecated in the future.
componentDidMount(){
let text = JSON.parse( this.blogContent );
this.setState({blog__: text });
}
Edit: Only use setState in componentDidMount according to #brandNew comment

How does one access state on a nested React component wrapped by an HOC?

I'm using Enzyme, and we can actually use the example component given in the docs as a foundation for my question.
Let's assume this <Foo /> component uses a <Link> component from ReactRouter and thus we need to wrap it in a <MemoryRouter> for testing.
Herein lies the problem.
it('puts the lotion in the basket', () => {
const wrapper = mount(
<MemoryRouter>
<Foo />
</MemoryRouter>
)
wrapper.state('name') // this returns null! We are accessing the MemoryRouter's state, which isn't what we want!
wrapper.find(Foo).state('name') // this breaks! state() can only be called on the root!
})
So, not exactly sure how to access local component state when using <MemoryRouter>.
Perhaps I'm performing an ignorant test? Is trying to get/set component state bad practice in testing? I can't imagine it is, as Enzyme has methods for getting/setting component state.
Just not sure how one is supposed to access the internals of a component wrapped in <MemoryRouter>.
Any help would be greatly appreciated!
So it seems with the latest release of Enzyme there is a potential fix for this issue of accessing state on a child component.
Let's say we have <Foo> (note the use of React Router's <Link>)
class Foo extends Component {
state = {
bar: 'here is the state!'
}
render () {
return (
<Link to='/'>Here is a link</Link>
)
}
}
Note: The following code is only available in Enzyme v3.
Revisiting the test code, we are now able to write the following
it('puts the lotion in the basket', () => {
const wrapper = mount(
<MemoryRouter>
<Foo />
</MemoryRouter>
)
expect(wrapper.find(Foo).instance().state).toEqual({
bar: 'here is the state!'
})
})
Using wrapper.find(Child).instance() we are able to access Child's state even though it is a nested component. In previous Enzyme versions we could only access instance on the root. You can also call the setState function on the Child wrapper as well!
We can use a similar pattern with our shallowly rendered tests
it('puts the lotion in the basket shallowly', () => {
const wrapper = shallow(
<MemoryRouter>
<Foo />
</MemoryRouter>
)
expect(wrapper.find(Foo).dive().instance().state).toEqual({
bar: 'here is the state!'
})
})
Note the use of dive in the shallow test, which can be run on a single, non-DOM node, and will return the node, shallow-rendered.
Refs:
https://github.com/airbnb/enzyme/issues/361
https://github.com/airbnb/enzyme/issues/1289
https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3
Thought it might be useful for you guys, as I stumbled upon this and have a fix.
In my case I have a component which is connected to redux.
class ComponentName extends Component {
...
}
export default connect(
mapStateToProps,
{
...
}
)(ComponentName );
connect() is obviously a HOC component.
So how do we access the "ComponentName" here?
Very simple:
component
.find(ComponentName)
.children()
.first()
.props() // returns ComponentName's props

Dynamic path segment OR 404

I have an app that needs to check with a backend API before rendering 404. The routing flow works something like this:
Request comes in to /{INCOMING_PATH}, and the application attempts to fetch and render data from api.com/pages/{INCOMING_PATH}.
If the API returns 404, then the app should return 404. If not, the data is rendered.
I'm not sold on using for this use case. {INCOMING_PATH} will be dynamic, potentially with slashes and extensions in the path. Is this possible to implement in React Router (with proper SSR behavior too)? If so, how should I proceed?
(This question was originally posted on github by another user. They were requested to post it here as it is a support request. But it doesn't seem they did. I am now stuck on exactly the same issue.)
I've solved this with the React Nested Status module.
I'm using https://github.com/erikras/react-redux-universal-hot-example so this code is geared towards that. See React Nested Status for a more generic solution.
Edits to server.js:
at the top
import NestedStatus from 'react-nested-status';
at the bottom replace:
const status = getStatusFromRoutes(routerState.routes);
if (status) {
res.status(status);
}
res.send('<!doctype html>\n' +
ReactDOM.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>));
with:
const repsonse = ReactDOM.renderToString(
<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>
);
const status = getStatusFromRoutes(routerState.routes);
if (status) {
res.status(status);
}
const nestedStatus = NestedStatus.rewind();
if (nestedStatus !== 200) {
res.status(nestedStatus);
}
res.send('<!doctype html>\n' + repsonse);
Then in what ever container/component you need to serve a 404 :
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import connectData from 'helpers/connectData';
import { fetchApiData } from 'redux/modules/foo/fetchApiData';
import { NotFound } from 'containers';
#connectData(null, (getReduxState, dispatch, state, params) => {
return dispatch(fetchApiData(params.fooId));
})
#connect(
(reduxState) => ({
fooData: reduxState.foo.data,
})
)
export default class ProductType extends Component {
static propTypes = {
fooData: PropTypes.object,
}
render() {
let content;
// ... whatever your api sends back to indicate this is a 404
if (!this.props.fooData.exists) {
content = <NotFound/>;
} else {
content = (
<div className={styles.productType}>
Normal content...
</div>
);
}
return content;
}
}
Finally replace /src/containers/NotFound/NotFound.js
import React, { Component } from 'react';
import NestedStatus from 'react-nested-status';
export default class NotFound extends Component {
render() {
return (
<NestedStatus code={404}>
<div className="container">
<h1>Error 404! Page not found.</h1>
</div>
</NestedStatus>
);
}
}
I'm not sure what kind of state implementation you are using. But, if you are using redux, then I think the simplest way is to use redux-simple-router. With it, your Routes are synchronized within your state, so you can dispatch action creators to change the router path. I would try to update satate with action creators instead of pushing the state directly from a component. The truth point must be always the state, in your case I would act as follows:
The component that requires to fetch the data will be subscribed to the "dataReducer" which is the isolated state part that this component should care about. Maybe the initial state of dataReducer is an empty array. Then, in componentWillMount you dispatch an action like: dispatch(fetchDataFromApi)) If the response code is 404, then in the action fetchDataFromApi you can dispatch another action, that is just an object like this one:
{type:SET_NOT_FOUND_ERROR}
That action will be handled by the reducer dataReducer, and will return a new state with an object (consider Immutability) that will have a property error, which will be a string with the reason, or whatever you want.
Then, in componentWillReceiveProps method, you, can check if the nextProps have or not have an error. If Error, you can render your error component, or even dispatch an action to go to the error page handled by react-router.
If no error, then you can dispatch an action (thanks to redux-simple-router) to go to the path y

Where to load the server data

I'm using the react-router and navigate to a component that gets an ID in the URL and has to use this ID to get data from the server with the help of an action.
At the moment I'm calling the action creator in the componentWillMount hook.
This works so far, but brings a problem.
In the render method I have to check, if myData really exists with all its attributes, before I can really render.
#connect(state => {myData: state.etc.myData})
export default class extends React.Component {
componentWillMount = () => {
this.props.dispatch(
ActionCreators.getData(this.props.params.id)
)
}
render() {
if (this.props.myData.hasNotLoaded) return <br/>
...
}
}
Is there another way to get data into the store before rendering without manual checks?
You can subscribe to router's onEnter hook and dispatch actions from where.
const store = configureStore()
const routing = (<Router>
<IndexRoute onEnter={()=>store.dispatch(myLoadDataActionCreator())}/>
</Router>)
So you can avoid setState from previous answer and don't tie up component with redux.
You should create a call back, for example:
_onChange() {
this.setState(myStore.getData());
}
Then in the following react functions do the following:
componentDidMount() {
myStore.addChangeListener(this._onChange);
},
componentWillUnmount() {
myStore.removeChangeListener(this._onChange);
}
I assume you're using the mixins for the react-router, if not, take a look at the docs for it, they have some useful functions that are worth looking at.
I don't think you will need that if logic in the render() method, react will handle that with the virtual dom management and know when to load it and the data.