react higher order component - javascript

Im looking into higher order functions and i dont really understand how this part works.
say i have the following function:
const withAdminWarning = WrappedComponent => {
return props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<WrappedComponent {...props} />
</div>
);
};
const Info = props => (
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
);
const AdminInfo = withAdminWarning(Info);
ReactDOM.render(
<AdminInfo isAdmin={true} info="There are the details" />,
document.getElementById("app")
);
From my understanding of components, to access the props variable, you have to use either props, if its a stateless component, or this.props if it is a class component.
From where does the props come into play in the example above as i cant get access to it from the WrappedComponent or anywhere else apart from the return statement.

The Higher order Component returns a function which is a functional component. Am I right in thinking that foo(Info) means withAdminWarning(Info)?
So after calling withAdminInfo the AdminInfo Component looks basically like:
const AdminInfo = props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
</div>
);

Related

Pass true/false state from a child to the parent- React

I would like to change a true/false state in a child component and pass it to a parent component, where the state is actually defined. Right now, this results only in an error.
Parent:
const PostTemplate = ({ data }) => {
const [isSlider, setIsSlider] = useState(false);
return (
<Layout class="page">
<main>
<Views setIsSlider={isSlider} {...data} />
</main>
</Layout>
)}
</>
);
};
Child:
const Views = (data) => {
return (
<div
key="views"
className="post__gallery views"
>
{data.views.edges.map(({ node: view }) => (
<divy
onClick={() => setIsSlider(true)}
>
<GatsbyImage
image={view.localFile.childImageSharp.gatsbyImageData}
/>
<div
className="post__caption"
dangerouslySetInnerHTML={{
__html: view.caption,
}}
/>
</div>
))}
</div>
);
};
Right now, this puts out:
setIsSlider is not a function.
(In 'setIsSlider(true)', 'setIsSlider' is false)
Perhaps also relevant the console.log from React:
Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
You're passing isSlider as a prop. You should be passing setIsSlider.
Note that state management in React is a complex topic and you probably want to do some research into how it is done in general. Directly passing a state setting callback works, but it doesn't scale well to complex applications.
Instead of passing the state variable you have to pass the state function like this:
Parent
const PostTemplate = ({ data }) => {
const [isSlider, setIsSlider] = useState(false);
return (
<Layout class="page">
<main>
<Views setIsSlider={setIsSlider} {...data} />
</main>
</Layout>
)}
</>
);
};
Child
You've to use data.<function_passed_via_props> to access the setIsSlider function, like this:
const Views = (data) => {
return (
<div
key="views"
className="post__gallery views"
>
{data.views.edges.map(({ node: view }) => (
<divy
onClick={() => data.setIsSlider(true)}
>
<GatsbyImage
image={view.localFile.childImageSharp.gatsbyImageData}
/>
<div
className="post__caption"
dangerouslySetInnerHTML={{
__html: view.caption,
}}
/>
</div>
))}
</div>
);
};
To Fix: Change setIsSlider={isSlider} into setIsSlider={setIsSlider}.
However, in case you need to manage more than a few states across components. I would suggest using Redux. This can centralize your common-used states, you can access or update these states in any component using Redux.

React rerendering list

I have a react page that has several components on it with a state that shows a modal. I dont want all the components in the app to re render when the modal shows.
const CustomersPage = () => {
const [showModal, setShowModal] = useState(false);
const dataSource = [...omitted data]
return (
<>
<Modal
visible={showModal} />
<div>
<div>
<div>
<Button type="primary" onClick={() => setShowModal(true)}>
Create
</Button>
</div>
<CustomForm />
</div>
<CustomList dataSource={dataSource} />
</div>
</>
);
};
When the showModal value changes, the components CustomForm component and the CustomList component re renders but I dont want them to re render every time the modal shows because the list can have over 100 components. How can I do this?
Edit.
const CustomList = ({ dataSource }) => {
return (
<div>
{dataSource?.map(i => (
<CustomCard
id={i.id}
...other props
/>
))}
</div>
);
};
const CustomCard = ({
... props
}) => {
return (
<>
<Card
...omitted properties
</Card>
</>
);
};
You can wrap the List and Form components in a React.memo component.
This way they will only re-render when their props change their identity.
See:
https://scotch.io/tutorials/react-166-reactmemo-for-functional-components-rendering-control
You can avoid unnecessary re-rendering with memo and hooks like useMemo and useCallback if you are using FC. Or if your are in CC the your create your component pure component that prevent unnecessary render.
Make your function component memo by wrapping component with Reaact.memo, then this will help to check and render component if there is any changes down there in your this child component. Despite all hooks like useCallback and useMemo are also helpfull for optimization.
There are tons of the articles are there about the use cases of these hooks, go and have look at them. They are really helpful.
Thanks

Accessing properties of a react component from the parent

I want to bundle some data together with a component. Here is an example of a SFC that has a property called name. I do not want to use the property name with the component named MyFormTab. Instead I would like to access this property from the parent component and assign it to be displayed within the parent.
const MyFormTab = (props) => {
const name = props.name
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='text'></input>
</div>
</>
)
}
I would then like to render this component inside a parent and use the name property for another purpose
const ParentOfMyFormTab = () => {
const [currentTab, setCurrentTab] = useState(1)
const Tab1 = <MyFormTab name='Tab1' />
const Tab2 = <MyFormTab name='Tab2' />
return (
<form>
<div id="tabTitles">
<h2 onClick={setCurrentTab(1)}>Tab1.name</h2>
<h2 onClick={setCurrentTab(2)}>Tab2.name</h2>
</div>
{currentTab === 1 ? <Tab1 /> : <Tab2 />}
</form>
)
}
Instead of an SFC, I could also use a class I'm thinking.
class MyFormTab {
constructor(name){
this.name = name
}
render(){
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='email'></input>
</div>
</>
)
}
}
My project is predominantly using hooks however. My team lead(who doesn't know React much) will probably be hesitant towards mixing class components with hooks. I've read on other posts that hooks can basically replace class components in most situations. I don't know how hooks could be better, or even be used in this situation.
What do you think would be a good way to do what I am trying to do? Is putting SFC's with hooks and class components into the same project a good idea? Am I looking at this whole thing wrong?
Thank you
In react props are passed only from parent to child. So you can just have a parent with that name value and passed it down if you want to.
Edited my answer to respond to you edit.
const MyFormTab = (props) => {
const name = props.name
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='text'></input>
</div>
</>
)
}
const ParentOfMyFormTab = () => {
const [currentTab, setCurrentTab] = useState(1)
const Tab1 = <MyFormTab name=`Tab1` />
const Tab2 = <MyFormTab name=`Tab2` />
return (
<form>
<div id="tabTitles">
<h2 onClick={setCurrentTab(1)}>Tab1</h2>
<h2 onClick={setCurrentTab(2)}>Tab2</h2>
</div>
{currentTab === 1 ? <Tab1 /> : <Tab2 />}
</form>
)
}
To you question about mixing class based and function components. You can't use hooks with class based components so don't, and there is no need to. I think you should learn more about the basics of react. If you need to share data with other components, the data should be in the parent component, passed to children or in a React context.

How to pass React Component as props to another component

I am trying to call PopupDialog.tsx inside Content.tsx as a sibling of Item.tsx.
Previously PopupDialog.tsx is called inside C.tsx file but due to z index issue i am trying to bring it out and call it in Content.tsx
Is it possible to somehow pass the whole component(popupDialog and its parameters) in Content.tsx so that i could avoid passing back and forth the parameters needed for popupdialog in content.tsx.
Code in C.tsx where PopupDialog component is called.
const C = (props: Props) => (
<>
{props.additionalInfo ? (
<div className="infoButton">
<PopupDialog // need to take this code out and want to add in Content.tsx
icon="info"
callback={props.callback}
position={Position.Right}
>
<div className="popuplist">{props.additionalInfo}</div>
</PopupDialog>
</div>
) : (
<Button className="iconbutton"/>
)}
</>
);
Content.tsx where i would like to call PopupDialog.tsx with its parameters
const Content = (props: Props) => {
const [componentToRender, docomponentToRender] = React.useState(null);
const [isAnimDone, doAnim] = React.useState(false);
return (
<div className="ContentItems">
<PWheel agent={props.agent} />
{isAnimDone && (
<>
<Item {props.agent} />
{componentToRender &&
<PopupDialog/> //want to call here with all its parameters to be passed
}
</>
)}
</div>
);
};
Folder Structure
App.tsx
->ViewPort.tsx
->Content.tsx
->PWheel.tsx
->Item.tsx
->A.tsx
->B.tsx
->C.tsx
{props.additionalinfo &&
->PopupDialog.tsx
->PopupDialog.tsx
So if I understand the question correctly you want to pass one component into another so that you can use the properties or data of the passed componenet in your current component.
So there are three ways to achieve this.
1)Sending the data or entire component as prop.This brings disadvantage that even though components which don't require knowledge
about the passed component will also have to ask as a prop.So this is bascially prop drilling.
2)The other is you can use context api.So context api is a way to maintain global state variale.so if you follow this approach you don't need to pass data or componenet as props.Wherever you need the data you can inport context object and use it in componenet.
3)Using Redux library.This is similar to context api but only disadavantage is that we will have to write lot of code to implement this.Redux is a javascript library.
Let me know if you need more info.
You need to :
<>
<Item {props.agent} />
{componentToRender &&
<PopupDialog abc={componentToRender} /> //you must call in this component, in this case i name it is abc , i pass componentToRender state to it
}
</>
and then PopupDialog will receive componentToRender as abc, in PopupDialog , you just need to call props.abc and done .
If you need to know more about prop and component you can see it here
I think what you want to use is Higher-Order-Components (HOC).
The basic usage is:
const EnhancedComponent = higherOrderComponent(WrappedComponent);
Below is such an implementation that takes a component (with all its props) as a parameter:
import React, { Component } from "react";
const Content = WrappedComponent => {
return class Content extends Component {
render() {
return (
<>
{/* Your Content component comes here */}
<WrappedComponent {...this.props} />
</>
);
}
};
};
export default Content;
Here is the link for higher-order-components on React docs: https://reactjs.org/docs/higher-order-components.html
Make use of
useContext()
Follow this for details:
React Use Context Hook

How to access ref that was set in render

Hi I have some sort of the following code:
class First extends Component {
constructor(props){super(props)}
myfunction = () => { this.card //do stuff}
render() {
return(
<Component ref={ref => (this.card = ref)} />
)}
}
Why is it not possible for me to access the card in myfunction. Its telling me that it is undefined. I tried it with setting a this.card = React.createRef(); in the constructor but that didn't work either.
You are almost there, it is very likely that your child Component is not using a forwardRef, hence the error (from the React docs). ref (in a similar manner to key) is not directly accesible by default:
const MyComponent = React.forwardRef((props, ref) => (
<button ref={ref}>
{props.children}
</button>
));
// ☝️ now you can do <MyComponent ref={this.card} />
ref is, in the end, a DOMNode and should be treated as such, it can only reference an HTML node that will be rendered. You will see it as innerRef in some older libraries, which also works without the need for forwardRef in case it confuses you:
const MyComponent = ({ innerRef, children }) => (
<button ref={innerRef}>
{children}
</button>
));
// ☝️ now you can do <MyComponent innerRef={this.card} />
Lastly, if it's a component created by you, you will need to make sure you are passing the ref through forwardRef (or the innerRef) equivalent. If you are using a third-party component, you can test if it uses either ref or innerRef. If it doesn't, wrapping it around a div, although not ideal, may suffice (but it will not always work):
render() {
return (
<div ref={this.card}>
<MyComponent />
</div>
);
}
Now, a bit of explanation on refs and the lifecycle methods, which may help you understand the context better.
Render does not guarantee that refs have been set:
This is kind of a chicken-and-egg problem: you want the component to do something with the ref that points to a node, but React hasn't created the node itself. So what can we do?
There are two options:
1) If you need to pass the ref to render something else, check first if it's valid:
render() {
return (
<>
<MyComponent ref={this.card} />
{ this.card.current && <OtherComponent target={this.card.current} />
</>
);
}
2) If you are looking to do some sort of side-effect, componentDidMount will guarantee that the ref is set:
componentDidMount() {
if (this.card.current) {
console.log(this.card.current.classList);
}
}
Hope this makes it more clear!
Try this <Component ref={this.card} />

Categories

Resources