ReactJs - Access Wrapped State from HOC Wrapper - reactjs

Im trying to use HOC pattern instead of Object Oriented to avoid redundant code.
The situation is simple as explained below:
In HomePage I have something like this:
const WrappedComponent = Wrapper(Wrapped);
<WrappedComponent { ...this.props } />
In the Wrapper "component", I want to expose a method called foo_method that is an Ajax Call to a WebServer. The result must be written in the state of Wrapped Component.
Now, WrappedComponent can call foo_method but when inside Wrapper, I have no scope of Wrapped state and this.props contains the props of HomePage, not wrapper so I cant create a callback function in Wrapper to be called.
Am I forgetting something in the implementation of HOC?

I think your HOC should look something like this, every WrappedComponent will get result (null while loading and API response after) in the props also wrapped component can call exposed fetch function manually via props.fetchFunction().
function Wrapper(WrappedComponent) {
const fetchData = () => {
return fetch('something');
};
return class extend React.Component {
constructor(props) {
super(props);
this.state = {
result: null,
};
}
componentDidMount() {
fetchData().then((result) => {
this.setState({result});
});
}
render() {
return (
<WrappedComponent
result={this.state.result}
fetchFunction={fetchData}
{...this.props}
/>
);
}
}
}

Related

Best way to use composition for replacing child component methods in React?

class DropZoneComp extends React.Component <any, any>
{
onDrop (file) {
}
render() {
return ( <DropZone onDrop={this.onDrop.bind(this)}></DropZone>)
}
}
export default function DropZoneParent(props) {
const onDrop = () => {
}
return (
<DropZoneComp onDrop={onDrop}/>
)
}
The issue I realized is that the class component functions aren't props, so you can't really pass them, but maybe there's a way to do this without breaking anything. One issue is that it's not a prop so I can't really replace it, the other issue is if onDrop change class variable that are not props, and so if I want to pass onDrop from the parent component, I need to store the same non props or instance or state variables in the parent class. What are the best practices and the cleanest way to go about it, especially if onDrop has to call setState and change instance variable (ex: this.files, this.currentFile, etc.)?
You can pass functions as a prop but you have to do binding in the constructor and then you can call setState and change variables on each instance.
constructor(props) {
super(props);
this.onDrop = this.onDrop.bind(this);
}
onDrop (file) {
}
render() {
return ( <DropZone onDrop={this.onDrop}></DropZone>)
}

Confusion how HOC are used in React

I was learning React and came across something called higher order component and also found this code:
function withSubscription(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
// ... that takes care of the subscription...
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
Well, basically it is clear what HOC does it accepts a component and returns enhanced version of that accepted component. But what confuses me is that how the component can be returned since we do not call render method, that is, we just need to do this withSubscription(someComponent) and enhanced someComponent will be returned but we do not call render method which actually returns that enhanced component.How is that possible?
You are actually confusing HOC with a React Component.
The name is misleading.
HOC is not a concept unqiue to React. This is a concept that is derived from the world of functional programming.
In functional programming we have Higher Order Functions (HOF)
A HOF is a function that takes another function as a parameter.
Consider the following example:
function greaterThan(n) {
return m => m > n;
}
let greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));
// → true
greaterThan is higher order function, it accepts a function as a parameter.
.map,.filter in JS are too HOF. They take function as a parameter.
Similarily we have HOCs.
Formal definition of HOC
A higher-order component is a function that takes a component and
returns a new component.
Did you notice it?It is not a component. It is a function that takes a component as a parameter. It also returns a component.
Consider the following example:
const hocWrapper = (PassedComponent) =>
({ children, ...props }) =>
<PassedComponent {...props}>
{children.split("").reverse().join("")}
</PassedComponent>
const name = (props) => <span>{props.children}</span>
const reversedName = hocWrapper(name)
<reversedName>Hello</reversedName>
In the above example we have
HOC - hocWrapper
PassedComponent - React Component
Return value - React Component [with enhanced functionality]
hocWrapper is a simple function which takes PassedComponent as a parameter and returns an enhanced version of PassedComponent.

How to mock data with props in your React Functional Component

In my React application, i am developing a screen using Functional Component.
I want to populate the data on the screen with mock data until my API is ready. I basically have an Accordion with open state where i want to display the names. Right now, i am hardcoding name to populate that. Later, i will be replacing with the actual data coming from Store. So to do that i am defining the hard coded data like below in my component itself as after the API is ready, my props will have all the required data.
function MyComponent (props) {
props={
history: {},
data:[
{
name:’XYZ’,
},
{
name:’ABC’,
}
]
}
return (
<div>Content goes here </div>
)
}
This throws me error. I want to understand if i am doing this correctly as i need to read the data values inside my div.
Props are immutable, so you should not change them.
Instead, you could mock the props that you are passing to MyComponent
Example:
In the parent component:
function MyApp() {
const mockProps={
history: {},
data:[
name:’XYZ’,
]
}
return <MyComponent {...mockProps}/>
}
and in MyComponent
function MyComponent (props) {
// do something with props here
return <div>Content goes here </div>
}
It is probably best to mock this data as coming from parent container, that way when you add Redux library later, you can simply change /where/ props are being sent from.
e.g
in Parent JS:
const Parent = props => {
const [accordionData, setData] = useState(['#1', '#2', '#3'])
/ ... rest of component /
return <Accordion accordionData={accordionData} />
}
in
const Accordion = props => {
const { accordionData } = props // pull props from parent component.
const mapAccordionData = accordionData.map(el => { return <div key={el}>el</div> })
return mapAccordionData
}
Something like this should work.
ParentJS is feeding the accordion an array of data via parent->child props. Later this will be store->child props. Accordion (child) is then rendering that data to so the user can see it via divs.
You would pass props into a component. They should come from either a parent's component state or a store (like Redux store).
function MyComponent(props) {
return <div>{props.hello}</div>;
}
class ParentComponent extends React.Component {
state = {
hello: 'Hello World!',
}
render() {
return <MyComponent hello={this.state.hello} />;
}
}
You can also pass a function that changes the state of the parent making the props also change for MyComponent. It won't mutate them but rather return a new state and therefore pass a new set of props.
Let me know if you need any further explanation.

How to change the react state in testing

I have a below code,
export default class CampaignTrustSticky extends Component {
constructor(props) {
super(props);
this.state = {
showTrustBlock: false
};
}
render () {
let { showTrustBlock } = this.state;
return(
<section
className={classNames('trust-sticky', {
'pull-up': showTrustBlock
})}
>
</section>
)
}
}
in my test case i have used like this,
it('Render Campaing TrustKey', () => {
let wrapper = shallow(<CampaignTrustSticky />);
expect(wrapper.find('sectaion.pull-up')).not.toBe(null)
});
in this test it has been failed because of the default state value is set as false. So how can i change the state value as true from test case to succeed that case?
it('Render Campaing TrustKey', () => {
let wrapper = shallow(<CampaignTrustSticky />);
wrapper.setState({ showTrustBlock: true });
expect(wrapper.find('sectaion.pull-up')).not.toBe(null)
});
But your test code should test if your component works. You are changing the state in the test but the component doesn't change it`s state.
You should implement the functionality that change the state in your component and test this. For example a button press or something like that.
The answer by jonathanrz is correct. Although, your component isn't really written correctly. How can the state in that component change? It will always be false if it's written like that.
The component looks like something that should receive "showTrustBlock" as a prop, from a parent component.
So, the best thing would be to pass the "showTrustBlock" as a prop from a parent component, and then you can easily test it by just passing different props.
Also, if you do it like that, you can rewrite the component as a stateless functional component.

React / Redux Components not re-rendering on state change

I think this question has been answer several time but I can't find my specific case.
https://codesandbox.io/s/jjy9l3003
So basically I have an App component that trigger an action that change a state call "isSmall" to true if the screen is resized and less than 500px (and false if it is higher)
class App extends React.Component {
...
resizeHandeler(e) {
const { window, dispatch } = this.props;
if (window.innerWidth < 500 && !this.state.isSmall) {
dispatch(isSmallAction(true));
this.setState({ isSmall: true });
} else if (window.innerWidth >= 500 && this.state.isSmall) {
dispatch(isSmallAction(false));
console.log(isSmallAction(false));
this.setState({ isSmall: false })
}
};
componentDidMount() {
const { window } = this.props;
window.addEventListener('resize', this.resizeHandeler.bind(this));
}
...
I have an other component called HeaderContainer who is a child of App and connected to the Store and the state "isSmall", I want this component to rerender when the "isSmall" change state... but it is not
class Header extends React.Component {
constructor(props) {
super(props);
this.isSmall = props.isSmall;
this.isHome = props.isHome;
}
...
render() {
return (
<div>
{
this.isSmall
?
(<div>Is small</div>)
:
(<div>is BIG</div>)
}
</div>
);
}
...
even if I can see through the console that redux is actually updating the store the Header component is not re-rendering.
Can someone point out what I am missing ?
Am I misunderstanding the "connect()" redux-react function ?
Looking at your code on the link you posted your component is connected to the redux store via connect
const mapStateToProps = (state, ownProps) => {
return {
isHome: ownProps.isHome,
isSmall: state.get('isSmall')
}
}
export const HeaderContainer = connect(mapStateToProps)(Header);
That means that the props you are accessing in your mapStateToProps function (isHome and isSmall) are taken from the redux store and passed as props into your components.
To have React re-render your component you have to use 'this.props' inside the render function (as render is called every time a prop change):
render() {
return (
<div>
{
this.props.isSmall
?
(<div>Is small</div>)
:
(<div>is BIG</div>)
}
</div>
);
}
You are doing it well in the constructor but the constructor is only called once before the component is mounted. You should have a look at react lifecycle methods: https://reactjs.org/docs/react-component.html#constructor
You could remove entirely the constructor in your Header.js file.
You should also avoid using public class properties (e.g. this.isSmall = props.isSmall; ) in react when possible and make use of the React local state when your component needs it: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
A component is only mounted once and then only being updated by getting passed new props. You constructor is therefore only being called once before mount. That means that the instance properties you set there will never change during the lifetime of your mounted component. You have to directly Access this.props in your render() function to make updating work. You can remove the constructor as he doesn't do anything useful in this case.

Resources