React Redux render connected component - reactjs

I'm using the connect function React Redux to connect my store to a React component. After it is connected I want to render the component. This works:
class Init extends React.Component {
render() {
return (
<View /> // Is really more elaborate
)
}
}
Connect it to the store:
const InitPage = connect(
state => ({
}),
dispatch => ({
})
)(Init)
Now render it:
class App extends React.Component {
render() {
const content = <InitPage />
return (
{content}
)
}
}
Great. This all works. However...
When I place the connect inside a function and return the connect, as in:
const getInitPage = () => {
const InitPage = connect(
state => ({
}),
dispatch => ({
})
)(Init)
return InitPage
}
If I now try to render the component:
class App extends React.Component {
render() {
const content = getInitPage()
return (
{content}
)
}
}
I get an error:
Invariant Violation: React.Children.only expected to receive a single React element child.
What is the correct way to return a Redux "connected" component from a function?

In case you are returning the component from the function, you need to render it like
class App extends React.Component {
render() {
const Content = getInitPage()
return (
<Content/>
)
}
}
Also make sure the componentName starts with a uppercase character.

Related

How can I obtain class props from url and store in react-redux

so I am trying to pass params using route to a react component and also at the same time use Component class props. Here is what am doing
import { loadSchemes, } from '../../actions/schemes;
export class Schemes extends Component {
constructor(props) {
super(props);
const { match: { params } } = this.props;
this.state = {
client_id: params.pk,
}
}
componentDidMount() {
this.props.loadSchemes();
}
render(){
return(
<div>
{this.props.schemes_list.map((scheme,index)=><p key={index}>{scheme}</p>)}
</div>
)
}
}
const mapStateToProps = (state) => ({
schemes_list: state.schemes,
});
export default connect(mapStateToProps,{ loadSchemes,})(Schemes);
And I have a url to this component as
<Route path="/client/:pk/schemes" component={Schemes}/>
The problem is I get an error this.props.schemes_list is undefined and this.props.loadSchemes is undefined
please help am using react-redux
Obviousely in component from where you call Scheme, you import { Schemes }, an unconnected component, instead of Schemes - default connected component. Please check it.

How to access a ref using React-Redux >= v6.0?

in React-Redux >= v6.0 connnect() options support a new parameter, forwardRef: boolean.
If {forwardRef : true} has been passed to connect, adding a ref to the connected wrapper component will actually return the instance of the wrapped component.
So, in my HoC called 'WithFields' I write:
[...]
import Form from '.../components/form';
const WithFields = (arg1, arg2) => (WrappedComponent) => connect(mapStateToProps, {someMethod}, null, {forwardRef: true})(class extends React.Component {
[...]
render(
return(<WrappedComponent ref={ref => this.wrappedComponent = ref }/>)
)
}
[...]
let Customer = WithFields('a', 'b')(Form);
export default Customer;
Now, in a Ticket component, I would to get the Customer ref with a method, but how?
import Customer from '....';
class Ticket extends Component {
SOME_METHOD_TO_GET_THE_REF_OF_THE_HOC_COMPONENT() {
?????????
}
render() {
[....]
<Customer/>
}
}
Ok, re-checked also the React docs (https://reactjs.org/docs/refs-and-the-dom.html), this is how to implement ref to the wrapped component of an HoC that uses React-Redux for the state management.
hoc.js
[...]
import Form from '.../components/form';
const WithFields = (arg1, arg2) =>
(WrappedComponent) =>
connect(mapStateToProps, {someMethod}, null, {forwardRef: true})(class extends React.Component {
[...]
render(
return(<WrappedComponent ref={ref => this.formComponent = ref }/>)
)
}
[...]
let Customer = WithFields('a', 'b')(Form);
export default Customer;
ticket.js
import Customer from '....';
class Ticket extends Component {
constructor(props) {
super(props);
this.customer = React.createRef();
}
triggerCustomerMethod (e) {
let form = ref.current.formComponent; // THIS IS HOW YOU GET THE <Form/> component wrapped in <Customer/>
// example: get the <Form/> state and use it as argument for Hoc onSubmit() method (that updates the Redux store)
let state = form.state;
form.props.onSubmit(state, e);
};
render() {
[....]
<Customer ref={this.customer}/>
<Button onClick={this.triggerCustomerMethod.bind(this)} text="Save"/> // clicking on button we launch triggerCustomerMethod()
}
}
Pass this function from your Ticket component to the HOC as props
getRef = (refvalue) => { setState({value: refValue})}
and inside the HOC consume props like this
props.getRef(value of ref here)
Ticket component
const Ticket = () => {
const getRef = (value) => {
//do something with the value
}
return <Customer handleRef={getRef}/>
}
Customer Component
const Customer = props => {
OnChange = (refvalue) => {
props.handleRef(refvalue)
}
return {....}
}
For more detailed answer check this How to pass data from child component to its parent in ReactJS?

Why would this HOC class not return a rendered child component in React?

I have the following code that wraps a function for providing context to child components, however, when I try to render this, it fails, my wrapped component doesn't render, in React 16.6.3
import React from 'react'
export const WishlistContext = React.createContext(null)
const AddToWishListButtonWrapper = (WrappedComponent) => {
return class WishlistButton extends React.Component {
state = {token: null, wishlistItems: []}
render() {
const {token, wishlistItems} = this.state
return (
<WishlistContext.Provider value={wishlistItems}>
<WrappedComponent {...this.props} />
</WishlistContext.Provider>
)
}
}
}
export default AddToWishListButtonWrapper
Then in my other component it looks like this:
import AddToWishListButtonWrapper from 'src/components/wishlist_button'
...
<AddToWishListButtonWrapper>
<WishlistButton>
{' '}{wishlistSuccess ? 'Added!' : 'Add to Wishlist'}
</WishlistButton>
</AddToWishListButtonWrapper>
This renders nothing, however, if I make the following change to lowercase with my import in my component and my JSX, the wrapped component renders, while none of the lifecycle methods are triggered, which is baffling.
import addToWishListButtonWrapper from 'src/components/wishlist_button'
<addToWishListButtonWrapper>
<WishlistButton>
{' '}{wishlistSuccess ? 'Added!' : 'Add to Wishlist'}
</WishlistButton>
</addToWishListButtonWrapper>
You are not using the HOC correctly. You need to create an instance of the component like
import React from 'react'
export const WishlistContext = React.createContext(null)
const AddToWishListButtonWrapper = (WrappedComponent) => {
return class WishlistButton extends React.Component {
state = {token: null, wishlistItems: []}
render() {
const {token, wishlistItems} = this.state
return (
<WishlistContext.Provider value={wishlistItems}>
<WrappedComponent {...this.props} />
</WishlistContext.Provider>
)
}
}
}
export default AddToWishListButtonWrapper
const MyComp = () => (
<WishlistButton>
{' '}{wishlistSuccess ? 'Added!' : 'Add to Wishlist'}
</WishlistButton>
)
const WrapComponent = AddToWishListButtonWrapper(MyComp)
and render it like
<WrapComponent />
or instead of HOC you can use the render props pattern like
import React from 'react'
export const WishlistContext = React.createContext(null)
const AddToWishListButtonWrapper = (WrappedComponent) => {
return class WishlistButton extends React.Component {
state = {token: null, wishlistItems: []}
render() {
const {token, wishlistItems} = this.state
const { children } = this.props;
return (
<WishlistContext.Provider value={wishlistItems}>
{children()}
</WishlistContext.Provider>
)
}
}
}
export default AddToWishListButtonWrapper
<AddToWishListButtonWrapper>
{() => (
<WishlistButton>
{' '}{wishlistSuccess ? 'Added!' : 'Add to Wishlist'}
</WishlistButton>
)}
</AddToWishListButtonWrapper>
The HOC AddToWishListButtonWrapper is a javascript function. It expects you to call it as a function and provide a component as it's argument
You need to use it something like this (I made the case of AddToWishListButtonWrapper small as that's the convention) -
const EnhancedWishList = addToWishListButtonWrapper(WishlistButton)
And then render it like this -
<EnhancedWishList />
You can use HOC as follows:
class WishlistButton extends Component{
-- component logic
}
export default AddToWishListButtonWrapper(WishlistButton);

How to test two redux related components?

Suppose there are two components, one is action dispatching class, other one is a state receiving class.
Component1 has a method dispatch a action to change props1 redux state, then Component2 receives the props1 from redux state passing it to Component3.
Component1 and Component2 are combined under SuperComponent.
// Component1.js
class Component1 extends React.component {
method1 = () => {
this.props.action1({ props1: something });
}
render() {
return (
...
)
}
}
export default connect(
state => ({ ... }),
{action1}
)(Component1)
// Component2.js
class Component2 extends React.component {
render() {
return (
<Component3 props1={this.props.props1} />
)
}
}
export default connect(
state => ({ props1: state.reducer1.props1 }),
{}
)(Component2)
const Component3 = ({ props1 }) => (
<div props1={props1} >
...
</div>
)
// SuperComponent
const SuperComponent = () => {
<div>
<Component1>
<Component2>
</div>
}
How to test this logic by jest and enzyme? Using integrate testing or separated unit testing? Thanks in advance.
You should test each component separately for unit testing.
Component1: You should test that when you click a button or something, that Component1 is dispatching the right action.
Component2:, you should test that when you provide the props, it behaves as excepted.
You can export the Component2 separately to unit test it.
// Component2.js
export class Component2 extends React.component {
render() {
return (
<Component3 props1={this.props.props1} />
)
}
}
export default connect(
state => ({ props1: state.reducer1.props1 }),
{}
)(Component2)

React Higher Order Component that detects dom events that takes functional components as arg

I have a scenario where I want to create an HOC that detects mouse events (e.g. mouseenter, mouseleave) when they occur on the HOC's WrappedComponent, then pass the WrappedComponent a special prop (e.g. componentIsHovered). I got this working by using a ref callback to get the wrapped component instance, then adding event listeners to the wrapped instance in my HOC.
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
export default (WrappedComponent) => {
return class DetectHover extends Component {
constructor(props) {
super(props)
this.handleMouseEnter = this.handleMouseEnter.bind(this)
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.bindListeners = this.bindListeners.bind(this)
this.state = {componentIsHovered: false}
this.wrappedComponent = null
}
componentWillUnmount() {
if (this.wrappedComponent) {
this.wrappedComponent.removeEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.removeEventListener('mouseleave', this.handleMouseLeave)
}
}
handleMouseEnter() {
this.setState({componentIsHovered: true})
}
handleMouseLeave() {
this.setState({componentIsHovered: false})
}
bindListeners(wrappedComponentInstance) {
console.log('wrappedComponentInstance', wrappedComponentInstance)
if (!wrappedComponentInstance) {
return
}
this.wrappedComponent = ReactDOM.findDOMNode(wrappedComponentInstance)
this.wrappedComponent.addEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.addEventListener('mouseleave', this.handleMouseLeave)
}
render() {
const props = Object.assign({}, this.props, {ref: this.bindListeners})
return (
<WrappedComponent
componentIsHovered={this.state.componentIsHovered}
{...props}
/>
)
}
}
}
The problem is that this only seems to work when WrappedComponent is a class component — with functional components the ref is always null. I would just as soon place the WrappedComponent inside <div></div> tags in my HOC and carry out the event detection on that div wrapper, but the problem is that even plain div tags will style the WrappedComponent as a block element, which doesn’t work in my use case where the HOC should work on inline elements, too. Any suggestions are appreciated!
You can pass the css selector and the specific styles you need to the Higher Order Component like this:
import React, {Component} from 'react';
const Hoverable = (WrappedComponent, wrapperClass = '', hoveredStyle=
{}, unhoveredStyle={}) => {
class HoverableComponent extends Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
}
}
onMouseEnter = () => {
this.setState({hovered: true});
};
onMouseLeave = () => {
this.setState({hovered: false});
};
render() {
return(
<div
className={wrapperClass}
onMouseEnter= { this.onMouseEnter }
onMouseLeave= { this.onMouseLeave }
>
<WrappedComponent
{...this.props}
hovered={this.state.hovered}
/>
</div>
);
}
}
return HoverableComponent;
};
export default Hoverable;
And use Fragment instead of div to wrap your component:
class SomeComponent extends React.Component {
render() {
return(
<Fragment>
<h1>My content</h1>
</Fragment>
)
}
And then wrap it like this
const HoverableSomeComponent = Hoverable(SomeComponent, 'css-selector');

Resources