In reference this coding pattern:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Thing from '../components/Thing';
class ThingContainer extends Component {
render() {
return <Thing {...this.props} />;
}
}
function mapStateToProps(state) {
...
}
export default connect(mapStateToProps)(ThingContainer);
So it 1) imports a component(Thing), 2) creates another component (ThingContainer which is technically not a container) class to render that first component, and lastly using connect to finally export the container.
What's the difference with skipping step 2 above, and simply using the imported component(Thing) directly to export the container?
Yeah, that file looks like it's somewhat unnecessary. The class ThingContainer component does nothing but forward props to <Thing>, which is exactly what the wrapper components generated by connect do already. So, that's useless - the file should just do export default connect(mapState)(Thing), and it would work exactly the same without the extra ThingContainer definition.
Related
I have inherited a project and are very new to React.
The problem is that I don't know how to extract an URL segment like 123456 from the URL page/123456.
Here is some code for I sidebar I want to use the URL segment in. I kept it short so it is as clear as possible where I want the URL segment.
import React, { Component } from 'react';
import { withRouter, Link } from 'react-router-dom';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { push } from 'connected-react-router';
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
URL segment:
</div>
);
}
}
export default compose(withRouter))(Sidebar);
If you have a router configured properly then you might be able to find that value as a string in the this.props.match.params. You can take a look to the following example(please note that it's using react hooks and the main difference for you would be to use this.props.match.params instead of useParams()).
P.S. you should call withRouter instead of passing it to the compose method. E.g. withRouter(Sidebar) and if you want to use it with connect I guess you can just call one inside the other: connect(...)(withRouter(Sidebar))
Writing unit testing in react using jest and enzyme. While checking with a component state , it throws an error "ReactWrapper::state() can only be called on class components ".
import React from 'react';
import { mount } from 'enzyme';
import expect from 'expect';
import CustomerAdd from '../CustomerAdd'
import MUITheme from '../../../../Utilities/MUITheme';
import { ThemeProvider } from '#material-ui/styles';
describe('<CustomerAdd />', () => {
const wrapper = mount(
<ThemeProvider theme={MUITheme}>
<CustomerAdd {...mockProps}></CustomerAdd>
</ThemeProvider>
);
test('something', () => {
expect(wrapper.find(CustomerAdd).state('addNewOnSubmit')).toEqual(true);
});
});
In the above code CustomerAdd Component is class component.I don't what wrong with my code. Can any one help me out of this problem. Thanks in advance.
So your default export
export default withStyles(styles)(CustomerAdd);
exports functional(HOC) wrapper about your class-based component. And it does not matter if name of class and import in
import CustomerAdd from '../CustomerAdd'
are equal. Your test imports wrapped version and after calling .find(CustomerAdd) returns that HOC not your class. And you're unable to work with instance.
Short time solution: export class directly as named export.
export class CustomerAdd extends React.Component{
...
}
export default withStyles(styles)(CustomerAdd);
Use named import in your tests:
import { CustomerAdd } from '../CusomerAdd';
Quick'n'dirty solution: use .dive to access your underlying class-based component:
expect(wrapper.find(CustomerAdd).dive().state('addNewOnSubmit')).toEqual(true);
It's rather antipattern since if you add any additional HOC in your default export you will need to monkey-patch all related tests with adding appropriate amount of .dive().dive()....dive() calls.
Long-term solution: avoid testing state, it's implementation details.
Instead focus on validating what's been rendered. Then you are safe in case of lot of different refactoring technics like replacing class with functional component, renaming state/instance members, lifting state up, connecting component to Redux etc.
I am learning react and stuck at a particular point. I want to have an utility js class that can be imported to most of the components. This utility class has to read the data from the store and then give the required information from the store.
import { connect } from 'react-redux';
class Test{
constructor(){
}
getData(){
// HOW TO GET THE DATA **HERE** ?
}
}
function mapStateToProps(state){
return { data: state.data};
}
export default connect(mapStateToProps)(Test);
Is it ok to have the Test as a ReactComponent with render() returning a null and using it as a utility function in other components ?
You are adding extra complexity to your code using this class.
import connect in any component you need and add add:
function mapStateToProps(state){
return { data: state.data};
}
export default connect(mapStateToProps)(Test);
you can access the state using props in that component. also you can use selectors for your state object and use that selectors in your component.
you can use this pattern: Redux Selector Pattern
The component which is external includes and external file
class NavigationBarContainer extends React.PureComponent {
render = () => <NavigationBar extraBanner={<Banner
/>} {...this.props} />
}
in my App
import NavigationBar from '../components/NavigationBar'
<NavigationBar
extraBanner />
doesn't seem to work
import NavigationBarContainer from '../components/NavigationBar'
<NavigationBarContainer {...this.props}>
doesnt seem to work either getting error below
**Invalid prop extraBanner of type boolean supplied to NavigationBar, expected a single ReactElement.**
Two possible things that are wrong here.
1) NavigationBarContainer is not being exported, thus you cannot import it.
You can fix this by making sure to export the class one of two ways -- either change the class declaration to include the export keyword
export default class NavigationBarContainer extends React.PureComponent
or add a line to the bottom of that file
export default NavigationBarContainer;
2) You are trying to import a component called NavigationBarContainer from a file called NavigationBar. If that file is called NavigationBarContainer then this will not work. Make sure that your file names are correct.
A quick summary of export vs export default and importing
export default
The default export can be given any name when imported, eg.
// components/MyComponent.js
export default class MyComponent extends React.Component {...}
// AnotherFile.js
import MyComponent from 'components/MyComponent'; // works
import WhateverName from 'components/MyComponent'; // also works
export
When you don't use the default keyword, then you're making a named export. These have to be imported directly by name, using this syntax:
// components/SmallComponents.js
export class SmallComponent1 extends React.Component {...}
export class SmallComponent2 extends React.Component {...}
// AnotherFile.js
import {SmallComponent1, SmallComponent2} from 'components/SmallComponents'; // works
import SmallComponent1 from 'components/SmallComponents' // does not work
import {WhateverName} from 'components/SmallComponents' // does not work
Hope this helps!
In my react app I have a component which was exported using react-redux connect.
class Test extends Component {
...
}
export default connect(...)(Test)
and using component:
<div>
<Test />
</div>
As long as I was exporting it in the same file everything was working fine. Because of tests issue I moved the connect to different file. Now it's like:
(Test.js)
class Test extends Component {
...
}
export default Test
and in different file (TestConnect.js):
import Test from './Test'
...
export default connect(...)(Test)
and using component:
<div>
<TestConnect />
</div>
As long as I had the Component and its connect in the same file changing props was rerendering the Test component. Now it's not. Could You please help me understanding that? How can I fix this?
Edit
Solved by use {pure:false} as "options" in connect!
Unless you're actually facing an edge case and this is the only way out, as stated in redux's troubleshooting section, using {pure: false} isn't the best solution, since redux expects your components to be pure (Always produce the same results, given the same props and states)
Here's how I'd do it:
Test.js
class Test extends Component {
...
render() { ... }
}
export default Test
TestContainer.js
import Test from './Test'
import connect from 'react-redux'
class TestContainer extends Component {
render() {
return(<Test {...this.props} /> (Don't forget to pass your props down, otherwise, Test won't work properly)
}
}
TestContainer = connect(...)(Test)
export default TestContainer
App.js
import TestContainer from './TestContainer'
class App extends Component {
...
render(){
return(
<div>
<TestContainer {...props (If you need to pass anything that isn't at redux store)} />
</div>
)
}
}
I've used this approach quite a few times in some projects and it's worked pretty well
If you're not in that edge case scenario and this solution still doesn't work, need some more information to see where things are going wrong :)