Testing mounted components with react-router v4 - reactjs

I'm migrating our application to react-router#4.x, and I'm having troubles with getting the tests that are using enzyme's mount function.
Whenever I mount a component that has a withRouter-wrapped sub-component, I run into trouble.
Here's an example of a simplified situation of what I'm testing:
class SomePage extends React.Component {
componentDidMount() {
this.props.getData();
}
render() {
return (
<div>
<ComponentWrappedInWithRouter />
</div>
);
}
}
const Component = (props) => (
<button onClick={() => this.props.history.push('/new-route') }>
Click to navigate
</button>
);
const ComponentWrappedInWithRouter = withRouter(Component);
Now, I want to test that SomePage calls its prop getData after it's mounted. Stupid example, I know, but the structure should be sufficient.
Whenever I write a test that uses enzyme's mount, I get the following error:
TypeError: Cannot read property 'route' of undefined
at Route.computeMatch (node_modules/react-router/Route.js:68:22)
at new Route (node_modules/react-router/Route.js:47:20)
Now, I think the reason this happens is because I try to call withRouter for a component that does not have router on the context - i.e. it has not been wrapped in <BrowserRouter /> or one of its siblings.
This wasn't an issue with react-router#3.x, but since the current major was a full rewrite, I totally understand that these issues will arise.
Any ideas as to how I can fix this?

Pretty easy: wrap your component being tested in a MemoryRouter from react-router-dom and you're good to go.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/MemoryRouter.md

Related

Giving React component animation on mount

I am trying to make this component move in when mounted but getting
Cannot read property 'enter' of undefined
Here is a simplified code (I have all the CSS classes ready):
class Example extends React.Component {
state = {
transitionIn: false,
};
componentDidMount = () => {
this.setState({ transitionIn: true })
}
render() {
return (
<CSSTransition
in={this.state.transitionIn}
timeout={1000}
className={'wordTransition'}
>
<div>dksjfnsdkjnfj</div>
</CSSTransition>
);
}
}
https://codesandbox.io/s/rj5046zxoo
I believe that the error you were experiencing is one that you solved in the codesandbox.io link you provided above. I was having this same problem. Instead of naming the prop that takes a class name to be used as the prefix for the various transition states classNames (plural) I was using the more familiar className (singular).
To reiterate: inside the <CSSTransition> component, make sure you are using a classNames prop and not className as you would inside of a react component's html elements.
I feel that the choice on the part of the React Transition Group to use a prop called classNames in their component is confusing and should perhaps be reconsidered.

How to change state of component from anywhere without Redux?

Is this bad practices or not ?
export state change function from component
import it from other file.
call the function to change state?
In this way we can change some component state from anywhere.
For example...
We want to change the Model.js state from anywhere.
Modal.js
import React from 'react';
export let toggleModal;
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
toggleModal = this.toggleModal;
}
toggleModal = () => {
this.setState({ open: !this.state.open });
};
render() {
const { open } = this.state;
return <div style={{ color: 'red' }}>{open && 'Hello Modal'}</div>;
}
}
App.js(Some Top Level component)
import React from 'react';
import Modal from './Modal';
export default () => (
<>
...
<Modal />
...
</>
);
Somewhere.js
import React from 'react';
import {toggleModal} from './Modal';
export default () => (
<>
<h1>Hello!</h1>
<button onClick={() => toggleModal()}>open Modal!</button>
</>
);
  
But there is no reference in React Official docs, so is this bad practices ?
What React Docs recommends...
Just passing function props to change parent state from parent to children
Use context
Redux or Mobx
But, these are too complex for me.
Example code here
https://next.plnkr.co/edit/37nutSDTWp8GGv2r?preview
Everything seems pretty much overwhelming and difficult at the beginning. But as we get out hands on them, it's give us more confidence to dig into.
I would recommend to use redux that's how we tackled props drilling problem. You can dispatch a action and connect reducer to corresponding component which upon updating state will re render. This is what I recommend to most of the people to learn the tale of redux with a real life example:
Understanding Redux: The World’s Easiest Guide to Beginning Redux
Apart from this you can take Dan Abramov, author of the library, free redux course on egghead.io:
Getting Started with Redux
The problem you run into, almost immediately like your code example does is this:
It will not work: your toggleModal() method expects a this to refer to an actual component instance. When your onClick() handler fires you invoke toggleModal() as a plain function. The this context will be wrong, and so at best (in your example) you will get an error because you try to invoke something undefined, at worst (in general) you end up invoking the wrong method.
When you think about it, for any non-trivial React component you will have a hard time obtaining a reference to the actual instance that is currently being used: you have to make sure that you are not forgetting to invoke the method on the right component instance and also you have to consider that instances may be created/destroyed 'at will' for whatever reason. For example: what if your component is rendered indirectly as part of some other component's render() method? Multiple layers of indirection like that make it even harder.
Now, you could fix all that by abusing ref with abandon but you will find that now you have to keep track of which ref refers to what particular instance, if you happen to have multiple of the components to consider in one render tree...
Whenever you think one component needs to handle the state of its siblings, the solution is usually to lift the state one level up.
export default class Modal extends React.Component {
render() {
const { isOpen } = this.props;
return <div style={{ color: 'red' }}>{isOpen && 'Hello Modal'}</div>;
}
}
export default class Home {
this.state = {
isOpen: false,
};
toggleModal = () => {
this.setState({ isOpen: !this.state.isOpen });
}
render() {
const { isOpen } = this.state;
return (
<>
<h1>Hello {name}!</h1>
<button onClick={() => this.toggleModal()}>open Modal!</button>
<Modal isOpen={isOpen}/>
<p>Start editing and see your changes reflected here immediately!</p>
</>
)
}
}
This way the Home handle the state and your problem is solved.
This can get annoying if the state needs to be "drilled down" to children, that's a problem than redux or react-context can solve.
Here <Modal /> is the child component. So to call a function in a child component you can simply use Ref.
You can refer this page to get more info about Ref.
You can assign a class variable as a ref to this child and use this class variable as an object to call its function.
I found if in special case, my way is okay.
Special case means something like customAlert component.
It is okay only one instance of customAlert component mounted at a time in App.
To achieve this...
1.Use ref to access and change DOM
2.attach state changing function or component to window and call window.function
3.my case: export state changing function and import it from other file.
And here is how to do with react Context
https://next.plnkr.co/edit/EpLm1Bq3ASiWECoE?preview
I think Redux is overkill if the main thing you are interested in is to make some states-like data available and updatable throughout your App without props drilling.
For that purpose, a much simpler approach (maybe not available at the time the question was posted?) is to use react context: https://frontend.turing.edu/lessons/module-3/advanced-react-hooks.html
"context - an API given to us by React, allowing for the passing of
information to child components without the use of props
[...]
useContext - a react hook, allowing functional components to take
advantage of the context API"

How to trigger manually a change of the route, with react-router-v4, like <Link> do it, but manually inside a function?

I'm working on a project with React and react-router-v4, and
I'm trying to find a way to trigger a change of the route manually.
something like <Link to="/newpage"> component, which takes you to a new route.
but I want to do it manually from inside one of my methods,
something like this.router.location = "/newpage".
Use the this.props.history.push method that is exposed by the <Link> component or by the withRouter high-order component as PompolutZ mentioned in the answer above.
An example of such is <button onClick={() => this.props.history.push('/Home') } />
What about using withRouter high-order component?
https://reacttraining.com/react-router/web/api/withRouter
class GoToNewPageButton extends React.Component {
navigateToNewPage() {
this.props.history.push('/newpage');
}
render() {
return <button onClick={this.navigateToNewPage.bind(this)}>To the new page!</button>
}
}
const GoToNewPageWithHistory = withRouter(GoToNewPageButton);
Render a Redirect component which will navigate to the new location.
<Redirect to="/somewhere/else"/>
For more details check documentation here.

React Router v4 lifecycle events

Upgrading my application to React Router v4 I'm struggling to find a clean way to implement the old onUpdate logic which I previously used the hide a popup menu on navigation.
The only way I can see in the documentation is to take advantage of the route render method but it seems much more complicated than before - any easier solutions?
const HidePopupThenRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={matchProps => {
hidePopup();
return <Component {...matchProps}/>
}}/>
)
<HidePopupThenRoute path="/" component={MyComponent}/>
I wouldn't consider that too complicated, but I can understand it not looking right to make imperative calls inside of a render function.
One alternative would be to create an OnUpdate component that listens for location changes and calls some function when the happen.
You can see the source code for an implementation of this that I wrote. You can either use that or replicate your own component with similar functionality. Basically, all that it does is subscribe to the history object and calls whatever function you pass to it when the location changes.
const MyComponent = () => (
<div>
<OnUpdate call={hideProps} />
<h1>My Component</h1>
<p>...</p>
</div>
)
A relatively clean solution was suggested on GitHub. What it comes down to is that you read the Router's history from React's Context API, where it gets populated by React Router. You can do so by defining the contextTypes property in your component:
static contextTypes = {
router: PropTypes.object
};
That will make sure that the Router is attached to that component. You can then use that the access the router and its history from the context, and add a callback that gets executed on history changes:
this.context.router.history.listen(hidePopup)
You'll probably want to do that in componentDidMount.

How can I find all nested Components using React/Redux?

I am looking to validate a form with Redux. I am trying to use make a form component which will iterate through children and find various input components (not to be confused with a native <input>.
I know there are a lot of open source solutions, but I'd like to understand some mechanics before jumping into picking any. I have a Form component setup to test like this:
import React from 'react';
export default class Component extends React.Component {
componentDidMount() {
this._iterate(this.props.children);
}
render(){
return (
<form {...this.props}>{this.props.children}</form>
);
}
_iterate(children) {
React.Children.forEach(children, child => {
console.log(child);
if (child.props.children) {
console.log('get children');
this._iterate(child.props.children);
}
});
}
};
I then have another Component with a render like this:
render() {
return (
<div>
<Form>
<ComponentA />
<ComponentB />
</Form>
</div>
);
}
Now ComponentA or ComponentB might have a component that nests more components down the line. Within those components would be a React component I have made for Text, Select, etc.
The code above would just console.log the components, and any children of them, that are in this specific render. It does not jump down into ComponentA children.
Is there a solution to that?
This isn't a problem you really want to solve.
The power in react is largely around the design pattern it encourages, and what you're doing is breaking that pattern; Component's should only talk to their immediate children and respond to their immediate parents. If you need to go deeper than that, then the component in the middle needs to be responsible for passing that data.
Rather than trying to dig into the innards of ComponentA and ComponentB, those component's themselves should have the accessibility props that you need. I.e., <ComponentA onChange={whatever} errorMessage={whatever}/> etc. and then hooking those props to their children should occur within ComponentA.

Resources