As the title suggest I want to be able to wrap a function (which contains) hooks in a HoC.
In the example below I want to be able to wrap the JSX from TestView with div element tag where the className="someClassName". However I get the following exception:
Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app See for tips about how to debug and
fix this
import React, { Component } from 'react'
function wrap(component) {
let calledComponent = component()
return (
<div className="someClassName">
{calledComponent}
</div>
);
}
function TestView() {
const [ val, setValue] = React.useState('Initial Value');
return (
<div>
<input type="text" value={val} onChange={event=>setValue(event.target.value)}/>
</div>
)
}
export default wrap(TestView);
Concretely, a higher-order component is a function that takes a
component and returns a new component.
react docs
so, you have to have a function that returns a component, maybe like this.
import React, { useState } from 'react';
import '../styles.css';
const withStyle = WrappedComponent => {
return function WithStyle() {
return (
<div className='myClassStyle'>
<WrappedComponent />
</div>
);
};
};
function TestView() {
const [val, setVal] = useState('Initial Value');
return (
<div>
<input type='text' value={val} onChange={e => setVal(e.target.value)} />
</div>
);
}
export default withStyle(TestView);
Related
In functional components
I want to share a function I have with a few components, to prevent duplicated code. All I need is the state that the function changes.
Is it possible to get the state from UpdateNum.jsx component to other components (as I showed in the code) ?
Or, is it possible to pass this function to other components so I can use it multiple times ? ( also to send props to the function)
import { useEffect, useState } from 'react';
export default function UpdateNum(change) {
const [numberCon , setNumber ] = useState(0);
useEffect(() => {
setNumber(numberCon + 1)
},[change])
return numberCon;
}
I want to get the numberCon state in Shop.jsx inside the value={} attribute
import UpdateNum from './functions/UpdateNum';
<div className='cart-icon' value={''}>
<FaShoppingCart size={40} />
</div>
And also at Items.jsx as I do -
import UpdateNum from './functions/UpdateNum';
return (
<div >
<h1 style={{ marginLeft: '30px' }}>
<UpdateNum/> - Items <br />
</h1>
</div>
);
UpdateNum function should return a component , if you want that function to return the actual state value , then you need to make it as a custom hook.
prefix the function name with use so that react knows that it's a custom hook.
import { useEffect, useState } from 'react';
export default function useNumberCon() {
const [numberCon , setNumber ] = useState(0);
function riseNum() { //Some function to calculate a number
setNumber(numberCon + 1)
}
return { numberCon, riseNum};
}
Now in the component where you want to consume this numberCon and riseNum ( Shop.jsx and Items.jsx ) you can do this
// In Shop.jsx
const { numberCon, riseNum } = useNumberCon();
......
......
<div className='cart-icon' value={numberCon}>
<FaShoppingCart size={40} />
</div>
If your <Shop /> and the <Items /> are sibling to each other then you need to add the custom hook call ( step #2 ) in the parent component which wraps them .
Reference:
Custom Hooks
Lifting State Up
1)Isolate your required function and return in new file.
export const updateNum = (reciveParam) => {
// do your calculation with reciveParam
return value
}
2)Import the function in required component
import updateNum from './util/updateNum';
const returnValue = updateNum(passParams);
3)Access here
<div className='cart-icon' value={returnValue}>
<FaShoppingCart size={40} />
</div>
If all the components share the same parent, you could pass the function as a prop: https://www.robinwieruch.de/react-pass-props-to-component
If they share different parents, or you dont want the hassle of passing the function around, you could make your own hook: https://reactjs.org/docs/hooks-custom.html#using-a-custom-hook
Is it possible to create an instance of a function component like regular classes like c# and JAVA where you can call functions on the component? Something like:
https://codesandbox.io/s/hungry-microservice-bp292?file=/src/App.js
It must be an instance so that the component can be used multiple places with its own instance and values. Not like a static class.
import React from "react";
import "./styles.css";
import MyFunc from "./MyFunc";
export default function App() {
const addAlert = () => {
MyFunc.addAlert("dasdsad");
};
return (
<div className="App">
<button onClick={addAlert}>Add alert</button>
<MyFunc />
</div>
);
}
You are mixing different concepts of ReactJS and the underlying DOM. It is not possible to get a ref on the functional component itself. At most you can use forwardRef to get a reference to the underlying DOM element. You can read more about that Refs and the DOM and Forwarding Refs.
With that in mind you could change your approach by uplifting the state to the parent e.g.
App.js
export default function App() {
const [alerts, addAlerts] = useState(["Alert1", "Alert2"]);
const addAlert = () => {
addAlerts(alerts.concat("dasdsad"));
};
return (
<div className="App">
<button onClick={addAlert}>Add alert</button>
<MyFunc alerts={alerts}/>
</div>
);
}
MyFunc.js
const MyFunc = props => {
return (
<>
{props.alerts && props.alerts.map((alert, index) => (
<div>{alert}</div>
))}
</>
);
};
export default MyFunc;
i have many components which have {props.children} deeply nested inside.
considery DRY principle is there a way to add this using some React pattern.
example
let's say i have two components,
Comp1.js
import React from "react";
const Comp1 = props => {
return (
<div>
<h1>{props.children}</h1>
</div>
);
};
export default Comp1;
Comp2.js
import React from "react";
const Comp2 = props => {
return (
<div>
<div>
<h1>{props.children}</h1>
</div>
</div>
);
};
export default Comp2;
if you see above code we have both Comp1 and Comp2 have line of code {props.children} repeated inside.
what i want now is some function which will add this line of code, something like below,
const addPropsChildrenToComp = (Comp)=>{
return(
(props)=>{
///do somehting here
}
)
}
const Comp1 = props => {
return (
<div>
<h1></h1>
</div>
);
};
Comp1WithPropsChildren = addPropsChildrenToComp(Comp1)
using HOC doesn't work because, in HOC we never modify passed component.
anyway to aceve this.?
to get more idea of my problem see this demo: https://codesandbox.io/s/trusting-http-pd1yu
in there i woul like to see CompWithPropsChildren component render props.children inside it.
I think I see what you're trying to get to, and you can accomplish this just using another component.
import React from "react";
import ChildComp from "./ChildComp";
const Comp1 = props => {
return (
<div>
<ChildComp {...props} />
</div>
);
};
export default Comp1;
import React from "react";
const ChildComp = props => {
return <h1>{props.children}</h1>
}
Assuming your ChildComp has some complex logic you don't want to duplicate, this will make it reusable for you.
I'm trying to prevent a re-render when using custom hook for hours now -.-, need some help ;O|
(Dont know if I should call this custom hook or functional hoc though)
I have a MessageList component that display a SimpleMessage wrapped in WithAvatarHeader.
Here is my profiler result:
Every time I add a message to the list, all messages are rendered again.
This isn't happening when I only use SimpleMessage in MessageList
Is there a way to memo(WithAvatarHeader) ?
MessageList :
import React from "react";
import SimpleMessage from "./SimpleMessage";
import WithAvatarHeader from "./WithAvatarHeader";
const MessageList = props => {
const Message = WithAvatarHeader(SimpleMessage);
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
SimpleMessage:
import React, { memo } from "react";
const SimpleMessage = props => {
return (
<div className="simple-message">
{props.message}
</div>
);
};
export default memo(SimpleMessage);
WithAvatarHeader:
import React from "react";
const WithAvatarHeader = WrappedComponent => props => {
return <WrappedComponent {...props} />;
};
export default WithAvatarHeader;
Thanks for the help :-)
You should not declare component inside another component.
Once you move declaration outside:
const Message = WithAvatarHeader(SimpleMessage);
const MessageList = props => {
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
you will be fine.
Reason is reconciliation process that decides what's to drop, what to create and what to update.
Besides your JSX says it still same element <Message> React checks component's constructor(it does not work with text representation from JSX). And it will referentially different(since you re-declare this constructor on next render). So React drops every <Message> and create them from scratch. Keeping declaration outside your MessageList means constructor is referentially the same so React will not re-create <Message> till key is the same.
Newbie to React and I need help again - everything was fine untill i shifted the code from App.js to separate component - and b/c it is the stateless component, i am using props and map function to access the value and state from App.js but it is not happy -- help please
import React from 'react';
const Recipes = props => (
<div>
{props.recipes.map(recipe => (
<div key={recipe.recipe_id}>
<img src={recipe.image_url} alt={recipe.title} />
<p>{recipe.title}</p>
</div>
))}
</div>
);
export default Recipes;
This just means that you don't pass in recipes properly as a prop where you render <Recipes />. Either recipes is null or incorrectly formatted.
ex:
// App.js
import React from 'react';
const App = () => {
const recipes = [{
recipe_id: '<id>',
image_url: '<some url>',
title: 'Lé title'
}];
// recipes could be null/undefined or not even passed as a prop
return (
<Recipes recipes={recipes} />
);
}
export default App;
So it's hard to know exactly what's happening without being able to see how you are passing down props, and exactly what data they contain. The error you are getting implies that you aren't actually sending the recipes array correctly.
I honestly never use stateless functions in react anymore, because PureComponent generally preforms better because of it's built in shouldComponentUpdate which prevents unnecessary re-renders. So here's how I would write that component:
import React, { PureComponent } from 'react';
class Recipes extends PureComonent {
recipeList = () => {
const recipes = this.props;
const recipeArray = recipes.map((recipe) => {
<div key={recipe.recipe_id}>
<img src={recipe.image_url} alt={recipe.title} />
<p>{recipe.title}</p>
</div>
});
return recipeArray;
}
render () {
return () {
<div>
{this.recipeList()}
</div>
}
}
}
export default Recipes;
That being said, my guess about the way you wrote your component is that if you were to console out props you would find that it was actually equal to recipes, which is why recipes.recipes is undefined.