Recompose multiple renderComponent - reactjs

I have a recompose filter that needs to render two components and pass props from a redux connect to the second component
However the code below never renders the second renderComponent - which is a real shame. Is there a way to get the below working, or should I opt for a regular React component?
import { compose, renderComponent } from "recompose"
import { connect } from "react-redux"
import Filters from "./filter/filter"
import Wrestlers from "./container"
const defaultState = state => ({
collection: state.roster,
})
export default compose(
renderComponent(Filters),
connect(defaultState),
renderComponent(Wrestlers),
)(Wrestlers)

renderComponent always discards the second argument (the base component) and renders the first argument. If you want to render then both, just create a new component and render them. Probably something like:
const Parent = ({ collection }) => (
// You can return an array here if you are using React 16
<div>
<Filters />
<Wrestlers collection={collection} />
<div>
)
export default connect(defaultState)(Parent)

Related

How to use currying to create HoC in React and connect to the Redux store at the same time?

I'm using React and Redux with react-redux, and I'm creating in React a High order Component that I want to connect to the Redux store, like this:
const HoC = parameter => WrappedComponent =>
return class WithSelected extends Component {
// ..some use of 'parameter'
render() {
return <WrappedComponent />
}
[...]
const exportComponent = compose(
connect(mapStateToProps),
HoC
)
export default exportComponent;
and
import Component from '../Component'
import exportComponent from '../file'
const componentToExport = exportComponent('someValue')(Component);
Now, this approach gives this error:
TypeError: Object(...) is not a function
Btw if I don't use currying creating the Hoc, it works, like this:
const HoC = (parameter, WrappedComponent) => [etc]
and
import Component from '../Component'
import exportComponent from '../file'
const componentToExport = exportComponent('someValue', Component);
It works. But how can I use currying creating HoC in React and use Redux at the same time?
There is nothing wrong with currying an HOC. The following is a valid high order component
const withHoc = param => Component => props =>{
return <Component {...props} foo={param} />
}
Your problem is compose. Your right-most argument which will provide the signature for the resulting composed function is actually evaluating to another HOC not the component itself.
You should connect the returned component
const HoC = parameter => WrappedComponent => connect(...)(props =>{
/*...*/
})
Returning a class based component is the same as returning a functional one
const HoC = parameter => WrappedComponent => connect(...)(class X extends Component{
render(){ return <div /> }
})
Just isn't that pretty though

React.js / Redux: Functional Components, legacy state/props and do I have to connect every component that uses the store?

some questions about React.js and Redux:
Can functional components also take advantage of the store and the states saved therein? e.g maybe in combination with React hooks like useEffect()?
In general, I can combine multiple reducers to one rootReducer and createStore(rootReducer) with it, and then pass it to a Provider Component that wraps my Component with it, this way, the store should be globally available in my whole app, correct?
For every component that want to use the store / states, do I always have to import the 2 methods mapStateToProps() and mapDispatchToProps() from react-redux for every Component and then connect them? Or can I also do this on some top-level component and make the usage of redux available in all my components globally, like in question 2) with the store provider?
last question: Can I still use the this.state property in my Components or use them in parallel as an addition (e.g for this Component isolated states) and then get the props from this state as usual with this.state.someState or is this not possible anymore when I already use Redux? And in the same way, can I still use / pass props to my components and read them from my Components as well, or is everything managed by state now only? (Or has the passing of props to my children nothing to do with Redux)?
1) Yes functional components can take advantage of the store. Its arguably much cleaner to read since props can be destructured right away.
const MyComponent = ({ auth }) => {
const [display, setDisplay] = useState(false)
useEffect(() => {
if(auth.user){
setDisplay(true)
}
}, [auth.user])
return(
<div>
{ display ? "Content": "Please sign in" }
</div>
)
}
const mapStateToProps = (state) => {
return{
auth: state.auth
}
}
export default connect(mapStateToProps)(MyComponent)
2) That is correct. You can also use combineReducers() which in some ways is cleaner to read.
import { createStore, combineReducers } from "redux"
import authReducer from "./reducers/authReducer"
import postReducer from "./reducers/postReducer"
const store = createStore(combineReducers({
auth: authReducer,
post: postReducer
}))
export default store
Then import store, wrap your App.js in a Provider and give it a prop of that store.
3) Generally, if you want your component to have direct access to the store it is a recognized pattern to use connect() in each one. Whether you decide to use mapStateToProps() or mapDispatchToProps() is entirely dependent on what that component needs to do. It does not required that you use both, you can just define one or the other in the connect().
import React, { useState } from "react"
import { addPost } from "/actions/postActions"
import { connect } from "react-redux"
const Form = ({ addPost }) => {
const [text, setText] = useState("")
const handleSubmit = (e) => {
e.preventDefault()
addPost(text)
}
return(
<form onSubmit={handleSubmit}>
<input value={text} onChange={(e) => setText(e.target.value)}/>
</form>
)
}
const mapDispatchToProps = (dispatch) => {
return {
addPost: (text) => dispatch(addPost(text))
}
}
export default connect(null, mapDispatchToProps)(Form)
4) You might have noticed by now that in the context of components, redux-state is stored as props. They are entirely different and isolated streams of data. So state remains untouched and controlled by the component itself. You can still freely use methods like this.state.dog even when your component is connected to the store. This is the isolation between component-state and redux-state.
import React, { useState } from "react"
import { connect } from "react-redux"
class MyDogs extends React.Component{
state = {
dog: "Tucker"
}
render(){
return(
<div>
Component State Value: {this.state.dog} //Tucker
Redux State Value: {this.props.dog} //Buddy
</div>
)
}
const mapStateToProps = (state) => {
return {
dog: state.dog
}
}
export default connect(mapStateToProps)(MyDogs)

How to pass props (that getting from Redux) from one Component to another (React/Redux)?

How to pass props (that getting from Redux) from WrapperComponent to InnerComponent (React/Redux)?
import React, { Component } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import InnerComponent from "./InnerComponent ";
class WrapperComponent extends Component {
state = {
data: []
};
render() {
return (
<div>
<InnerComponent props={this.props} />
</div>
);
}
}
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps)(withRouter(WrapperComponent));
After rendered the WrapperComponent - the props are still not in.
Any LifeCicle Methods can't help to resolve it.
Is It Possible?
It is possible and it is recommended to do so, so you don't to need call all the HOC store each time via 'connect'. Call all concerned actions and reducers to your containers and pass them as props.
In this case, your reducer name is called data, you need to call it like this (I changed the props name to data, so you can call props.data to your child):
<InnerComponent data={this.props.data} />
Or you can pass all the props from the parents like this:
<InnerComponent {...this.props} />

Proper seperation of React component in container and presentational components for redux

I'm trying to seperate a component like mentioned in the title.
According to the redux tutorial for react it's a best practice to split components up.
Until now I have the following components:
ReduxTestNetwork
import React, {Component} from 'react';
import {Edge, Network, Node} from '#lifeomic/react-vis-network';
import { connect } from "react-redux";
import MyNetwork from "./MyNetwork";
...
const mapStateToProps = state => {
return { nodes: state.nodes,edges: state.edges };
};
const VisNetwork = ({nodes,edges}) => (
<MyNetwork nodes={nodes} edges={edges} options={options}/>
);
const ReduxTestNetwork = connect(mapStateToProps)(VisNetwork);
export default ReduxTestNetwork;
MyNetwork
import React, {Component} from 'react';
import {Edge, Network, Node} from '#lifeomic/react-vis-network';
import connect from "react-redux/es/connect/connect";
import {addNode} from "../actions";
const mapDispatchToProps = dispatch => {
return {
addNode: node => dispatch(addNode(node))
};
};
class MyNetwork extends Component {
constructor(props) {
super(props);
this.state = {nodes: props.nodes, edges: props.edges, options:props.options};
}
componentDidMount() {
console.log('I just mounted')
//this.onClick();
}
onClick(e){
console.log(e)
console.log(this)
/* this.props.addNode({id:5,label:'Node 5'});
this.setState(prevState => ({
nodes: [...prevState.nodes, {id:5,label:'Node 5'}]
}));*/
}
render() {
const nodes = this.state.nodes.map(node => (
<Node key={node.id} {...node}/>
));
const edges = this.state.edges.map(edge => (
<Edge key={edge.id} {...edge}/>
));
return (
<div id='network'>
<Network options={this.state.options} ref={(reduxTestNetwork) => {
window.reduxTestNetwork = reduxTestNetwork
}} onSelectNode={this.onClick.bind(this)}>
{nodes}
{edges}
</Network>
</div>);
}
}
const SVNetwork = connect(null, mapDispatchToProps)(MyNetwork);
export default SVNetwork;
I connected ReduxTestNetwork to the store to obtain the state as props and MyNetwork to be able to dispatch.
I read that presentational components should only be used to display elements and the container components should include the logic how and what to display. But I need in MyNetwork some logic also to interact with the Network component which uses a 3rd party library.
So my questions are:
Is my seperation correct?
Where should I put logic for (for example) calculating the size or color of displayed nodes?
Thanks in advance
Several things:
You don't need to use connect twice. Pass mapStateToProps and mapDispatchToProps at the same time, both on the container.
If you want to follow the path of purely presentational components, consider using a side effect library: refract, sagas, thunk... they have patterns to deal with your logic outside of the component.
If you prefer a more hand made approach, you could move every method you need to the container and pass to the component via props only the data and the function references to modify it.

Passing Props between components

I am creating a bit of a playground to learn react and I've hit a road block with passing props between components. I essentially have two components, 1 that is the base component and then another that renders it out on the page with some extras (which i've removed for simplicity sake). I essentially want to be able to reuse the items in other places.
I'd like to be able to, when rendering the component in the example specify if it is type=submit if nothing specified default to type=button.
I'm clearly missing the point here because I get the error Cannot read property 'props' of undefined with the below code. Any help would be appreciated
Button Component
import React, {PropTypes} from 'react';
import './button_component.scss';
const propTypes = {
type: PropTypes.string
}
const ButtonComponent = () => {
return <button type={this.props.type}>button</button>
}
ButtonComponent.propTypes = propTypes;
export default ButtonComponent;
Then I have a component that outputs my item
import React from 'react';
import ButtonComponent from './button_component';
import Example from './example'
export default () =>
<Example>
<ButtonComponent type='button' />
</Example>;
ButtonComponent is a functional component. Hence, you can not use this.props in its code.
You should introduce props argument
const ButtonComponent = (props) => {
return <button type={props.type}>button</button>
}
Object destructuring and defaultProps can help you make your code simpler
const ButtonComponent = ({ type }) => {
return <button type={type}>button</button>
}
ButtonComponent.defaultProps = {
type: 'button'
}
then you can simply put <ButtonComponent /> to render a button.

Resources