Why I can't apply redux's connect with material-ui withStyles? - javascript

I'm trying to add withStyles() hook from material-ui in the redux container-component with connect() function via { compose } function from 'recompose' and get this error from recompose package:
TypeError: Function.prototype.apply was called on #, which is a object and not a function
I ask for any help, I have already spent too much time on this
import { withStyles } from '#material-ui/core/styles';
import { styles } from './styles';
import { compose } from 'recompose';
import { connect } from 'react-redux';
...
function mapStateToProps(state) {
return {
someVal: state.someVal,
}
}
function mapDispatchToProps(dispatch) {
return ({
changeVal: () => {dispatch('CHANGE_VAL')}
})
}
export default compose(
withStyles(styles),
connect(mapStateToProps, mapDispatchToProps)(App)
);
//if i do:
export default connect(mapStateToProps,mapDispatchToProps)(App)
//or:
export default withStyles(styles)(App)
//it's work. (just to clarify)

connect and withStyles are both HOC
a higher-order component is a function that takes a component and returns a new component.
So you need to wrap App inside withStyles and connect
export default connect(mapStateToProps,mapDispatchToProps)(withStyles(styles)(App))

Related

How to make a component more readable if it has multiple props as arguments?

I have large component where I am passing data from parent via slots, everything works as expected but I have one big problem which is very irritating to me, How I can to destructure ({partDetails, setUniqValue, openModal, setData, setPrices, setBoxImages, setZoomImage, setSelectedPartDetails}) this arguments so that my component becomes slightly more attractive, I want to achieve this result using typescript, but I am not sure what is best practice for it.
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { MaterialIcons } from "#expo/vector-icons";
import { componentStyle } from '../styles';
import { PickOfferBoxHeader } from './PickOfferBoxHeader/PickOfferBoxHeader';
import { ConditionRow } from './Rows/ConditionRow';
import { WarrantyRow } from './Rows/WarrantyRow';
import { PriceRow } from './Rows/PriceRow';
import { GradeRow } from './Rows/GradeRow';
import { AntDesign } from "#expo/vector-icons";
import { IIIpartDetails } from '../../../controllers/offers/interfaces';
import { IPickpartDetails, IuniqueValue, IPickData, Imodal } from '../../../screens/offers/interfaces';
import { ImagesRow } from './ImageRow/ImageRow';
interface IPickOfferBoxProps {
partDetails: IPickpartDetails,
setUniqValue: React.Dispatch<React.SetStateAction<IuniqueValue>>,
openModal: () => void,
setData: React.Dispatch<React.SetStateAction<IPickData[]>>
setPrices: React.Dispatch<React.SetStateAction<number>>
setZoomImage: React.Dispatch<React.SetStateAction<Imodal>>,
setSelectedPartDetails:React.Dispatch<React.SetStateAction<IIIpartDetails | undefined>>
setBoxImages: any,
}
export const PickOfferBox: React.FC<IPickOfferBoxProps> = ({partDetails, setUniqValue, openModal, setData, setPrices, setBoxImages, setZoomImage, setSelectedPartDetails}) => {
return (
<p>
asdasdad
</p>
);
}
Consider component composition in react.
Or you might try something like effector in react to communicate between components, or create local store for this component and it will not need props, you gonna derive them from store.

react redux way of printing props

i am trying to include my common component in my main.js
this one I did it it successfully.
but in my common component, I am trying to print my redux data values.
so I created a method called handleClickForRedux to print the values.
I have included mapStateToProps and mapDispatchToProps
but still value is not printing at this line. console.log("event reddux props--->", props);
can you tell me how to fix it.
providing my code snippet and sandbox below.
https://codesandbox.io/s/react-redux-example-265sd
scroll.js
import React, { useEffect, useState, Fragment } from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import Card from "#material-ui/core/Card";
//import CardActions from "#material-ui/core/CardActions";
import CardContent from "#material-ui/core/CardContent";
import Typography from "#material-ui/core/Typography";
import Drawer from "#material-ui/core/Drawer";
import { bindActionCreators } from "redux";
import * as actionCreators from "../actions/actionCreators";
import { connect } from "react-redux";
import { compose } from "redux";
function SportsMouse(classes, props) {
// const [canEdit, setCanEdit] = useState(false);
function handleClickForRedux(event) {
console.log("event--->", event);
console.log("event reddux props--->", props);
}
return (
<Card>
<div onClick={handleClickForRedux}>I am here </div>
</Card>
);
}
SportsMouse.propTypes = {
classes: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
export default compose(
connect(
mapStateToProps,
mapDispatchToProps
)
)(SportsMouse);
main.js
import React from "react";
import { Link } from "react-router-dom";
import Scroll from "../commonComponents/scroll";
const Main = props => {
const { children, match, ...rest } = props;
return (
<div>
<h1>
<Scroll />
<Link to="/">Reduxstagram</Link>
</h1>
{React.Children.map(children, child => React.cloneElement(child, rest))}
</div>
);
};
export default Main;
Even when using material-ui, components only accept one argument. classes exists inside props. If you console.log(classes) you'll see that it contains all of your props, including material-ui's styles. It should be this:
function SportsMouse(props) {

React: Using a redux export connect from other components

I'm writing a mobile app with React Native. I have two js files as following:
Error.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getTranslate } from 'react-localize-redux';
function mapStateToProps(state) {
return {
t: getTranslate(state.locale)
};
}
export default connect(
mapStateToProps
)(
({ t }) => ({
e001: t('wrong_format'),
e002: t('invalid_email'),
})
);
SignIn.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getTranslate } from 'react-localize-redux';
import { Field, reduxForm } from 'redux-form';
import Error from './Error';
const validate = (values) => {
console.log('error: ', Error);
// Process validate redux-form with messages from Error.js
};
class SignIn extends Component {
// Process login form with redux-form
}
function mapStateToProps(state) {
return {
t: getTranslate(state.locale),
};
}
const SignInForm = {
form: 'SignIn',
validate,
};
export default connect(
mapStateToProps
)(
reduxForm(SignInForm)(
SignIn
)
);
How can I use the data that exported from Error.js in SignIn.js? (e.g. values of 'e001', 'e002')
Example from validate function (in SignIn.js) I wanna show the value of code "e001" from Error.js.
For more detail, my idea is collect all error messages from language file (using react-localize-redux) into Error.js, then from validate functions of redux-form, i can show those messages easier.
Its look like you want Error.js as a helper file.But you are implemented it as react-redux container.
If you implementing it as react-redux container then necessarily it will call every time whenever store will change.
Instead simply export it.
import { getTranslate } from 'react-localize-redux';
const format = ({
e001: getTranslate('wrong_format'),
e002: getTranslate('invalid_email'),
})
export default format;
Called it as once imported
console.log(format.e001);

React-redux connect() fails to pass the props

I'm trying to learn react-redux architecture, and I failed on the most basic stuff.
I created class HomePage and used react-redux connect() to connect it to store's state and dispatch.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {HomeButtonClickAction} from "./HomeActionReducer";
import {connect} from "react-redux";
class HomePage extends Component {
constructor(props) {
super(props);
console.log('HomePage props');
console.log(this.props);
this.buttonClicked = this.buttonClicked.bind(this);
}
buttonClicked() {
console.log('button cliked');
this.props.buttonClick();
}
render() {
console.log('Re-rendering...');
let toggleState = this.props.toggle ? 'ON' : 'OFF';
return (
<div>
<button onClick={this.buttonClicked}>{ toggleState }</button>
</div>
)
}
}
HomePage.propTypes = {
toggle: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
};
const mapStateToProps = (state, ownProps) => {
return {
toggle: state.toggle
}
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
buttonClick: () => {
dispatch(HomeButtonClickAction());
}
}
};
const HomeContainer = connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
export default HomePage;
But it's not working for me. HomeContainer doesn't pass props to HomePage component.
I've got these warnings in devtools.
My index.js looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import AppReducer from "./reducers/AppReducer";
import { createStore } from "redux";
import { Provider } from 'react-redux';
const store = createStore(AppReducer);
ReactDOM.render(
<Provider store={ store }>
<App/>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
and AppReducer.js
import { combineReducers } from 'redux';
import { toggle } from '../home/HomeActionReducer';
const AppReducer = combineReducers({
toggle
});
export default AppReducer;
and HomeActionReducer.js
const HOME_BUTTON_CLICK = 'HOME_BUTTON_CLICK';
export function toggle (state = true, action) {
console.log('toggle launched');
switch (action.type) {
case HOME_BUTTON_CLICK :
return !state;
default:
console.log('Toggle reducer default action');
return state;
}
}
export function HomeButtonClickAction() {
console.log('action emitted');
return {
type: HOME_BUTTON_CLICK
};
}
Being a newbie I'll really appreciate your help :)
You are exporting HomePage, which is the presentational component. You want to export HomeContainer, which is the container that passes the props to HomePage through connect.
So replace this
export default HomePage;
with this
export default HomeContainer;
You can also directly write
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
Note that, since it's the default export, you can name the import as you want, eg.:
import HomePage from './HomePage' // even if it's HomeContainer that is exported
You have this:
const HomeContainer = connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
export default HomePage;
To create an instance of the connect component you need to do this:
export default connect()(HomePage);
Notice I did not write export default twice, bad practice, you only export default once per component so the connect() goes inside that same line of code and the invocation or second set of parentheses you wrap around the component you are working in.
This connect() function is actually a React component that you are going to pass some configuration to and the way you begin to do that is by calling mapStateToProps like so:
const mapStateToProps = () => {
};
export default connect()(HomePage);
You could also do:
function mapStateToProps() {
}
If you read it, it makes sense, this is saying that we are going to map our state object, all the data inside the redux store and run some computation that will cause that data to show up as props inside our component, so thats the meaning of mapStateToProps.
Technically, we can call it anything we want, it does not have to be mapStateToProps, but by convention we usually call it mapStateToProps and its going to be called with all the state inside of the redux store.
const mapStateToProps = (state) => {
};
export default connect()(HomePage);
The state object contains whatever data you are trying to access from the redux store. You can verify this by console logging state inside the function like so:
const mapStateToProps = (state) => {
console.log(state);
return state;
};
export default connect()(HomePage);
I am returning state just to ensure that everything is working just fine.
After defining that function, you take it and pass it as the first argument to the connect() component like so:
const mapStateToProps = (state) => {
console.log(state);
return state;
};
export default connect(mapStateToProps)(HomePage);
Thats how we configure the connect component.We configure it by passing it a function. Run that and see what happens.

Tips when testing a connected Redux Component

I've pasted my Component below which is very, very basic. When the Component is mounted, it will basically call the fetchMessage Action, which returns a message from an API. That message will in turn get set as state.feature.message in the mapStateToProps function.
I'm at a complete loss on where to begin testing this Component. I know that I want to test that:
1) The Feature Component is rendered
2) The fetchMessage function in props is called
3) It displays or has the correct message property when rendered using that
I've tried setting my test file up as you can see below, but I just end up with repeated error after error with everything that I try.
Could anyone point me in the right direction with what I'm doing wrong?
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';
class Feature extends Component {
static propTypes = {
fetchMessage: PropTypes.func.isRequired,
message: PropTypes.string
}
componentWillMount() {
this.props.fetchMessage();
}
render() {
return (
<div>{this.props.message}</div>
);
}
}
function mapStateToProps(state) {
return { message: state.feature.message };
}
export default connect(mapStateToProps, actions)(Feature);
Test file:
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import expect from 'expect';
import { shallow, render, mount } from 'enzyme';
import React from 'react';
import sinon from 'sinon';
import Feature from '../index';
const mockStore = configureStore([thunk]);
describe('<Feature />', () => {
let store;
beforeEach(() => {
store = mockStore({
feature: {
message: 'This is the message'
}
});
});
it('renders a <Feature /> component and calls fetchMessage', () => {
const props = {
fetchMessage: sinon.spy()
};
const wrapper = mount(
<Provider store={store}>
<Feature {...props} />
</Provider>
);
expect(wrapper.find('Feature').length).toEqual(1);
expect(props.fetchMessage.calledOnce).toEqual(true);
});
});
You can use shallow() instead of mount() to test your component. The shallow() method calls the componentWillMount() life-cycle method so there is no reason to use mount(). (Disclaimer: I am not quite well at mount() yet.)
For connected components, you can pass a store object like this:
<connectedFeature {...props} store={store} />
And you should call shallow() method twice to make it work for connected components:
const wrapper = shallow(<connectedFeature {...props} store={store} />).shallow()
Testing Connected React Components
Use separate exports for the connected and unconnected versions of the components.
Export the unconnected component as a named export and the connected as a default export.
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';
// export the unwrapped component as a named export
export class Feature extends Component {
static propTypes = {
fetchMessage: PropTypes.func.isRequired,
message: PropTypes.string
}
componentWillMount() {
this.props.fetchMessage();
}
render() {
return (
<div>{this.props.message}</div>
);
}
}
function mapStateToProps(state) {
return { message: state.feature.message };
}
// export the wrapped component as a default export
export default connect(mapStateToProps, actions)(Feature);
Remember connected components must be wrapped in a Provider component as shown below.
Whereas unconnected components can be tested in isolation as they do not need to know about the Redux store.
Test file:
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import expect from 'expect';
import { shallow, render, mount } from 'enzyme';
import React from 'react';
import sinon from 'sinon';
// import both the wrapped and unwrapped versions of the component
import ConnectedFeature, { feature as UnconnectedFeature } from '../index';
const mockStore = configureStore([thunk]);
describe('<Feature />', () => {
let store;
beforeEach(() => {
store = mockStore({
feature: {
message: 'This is the message'
}
});
});
it('renders a <Feature /> component and calls fetchMessage', () => {
const props = {
fetchMessage: sinon.spy()
};
const wrapper = mount(
<Provider store={store}>
<connectedFeature {...props} />
</Provider>
);
expect(wrapper.find('Feature').length).toEqual(1);
expect(props.fetchMessage.calledOnce).toEqual(true);
});
});

Categories