How can I get props in this case? - reactjs

I'm learning HOC, I have a question how is it possible to get props in HOC in this case.
withRainbow.js(HOC)
import React from 'react';
const withRainbow = (WrappedComponent) => {
const colors = ['red', 'blue', 'orange'];
const randomColor = colors[Math.floor(Math.random() * 3)];
const className = randomColor + '-text';
return ( props ) => {
return (
<div className={className}>
<WrappedComponent {...props}/>
</div>
)
};
}
export default withRainbow;
About.jsx
import React from 'react';
import withRainbow from '../hoc/withRainbox';
const About = () => {
return (
<div className="container">
<h4 className="center">About</h4>
<p>This is about yay!</p>
</div>
);
}
export default withRainbow(About);
How is it possible to get props through callback return (props) => ... in withRainbow.js even though withRainbow(About) in About.jsx has no argument props?
If About.jsx has state, can I get it too in withRainbow.js?

How is it possible to get props through callback return (props) => ...
in withRainbow.js even though withRainbow(About) in About.jsx has no
argument props?
withRainbow(About) returns a new component to which if you pass props while rendering, it can access props like you do in the withRainbow HOC event though you aren't access props in the About component.
const AboutWithRainbow = withRainbow(About);
...
return (
<AboutWithRainbow abc={'2433'} />
)
If About.jsx has state, can I get it too in withRainbow.js?
No you shouldn't access state of a child component in parent. If these is such a case, you must lift the state up

There is a confusion because you are looking at it in the wrong direction.
The props that the function receives are not from child component that you are exporting as withRainbow(About).
It is coming from the parent component that is calling withRainbow(About)
// In about component.
const AboutWithRainbow = withRainbow(About);
// In parent component
<AboutWithRainbow {...propsFromParent} />
These are the props being received in the HOC component.
This also answers the second question. No, you cannot get state as an argument because data flows down.

Related

Pass the data from child functional component to parent class component

I am quite new to react and I have been trying to figure out how to pass the data from child function to parent class component. In my child, I only have a variable that stores username from the URL. But I can't seem to figure out how to pass it to the parent component.
Here is my child.
const GetUsername = () => {
const params = useParams();
const [param, getParam] = useState(params.name);
return <p>{params.name}</p>;
};
export blabla
I would like to know how I would have to access that params.name in my parent component.
Here is my parent.
export class test extends Component {
constructor(props) {
super(props);
this.data = React.createRef();
}
render() {
console.log(this.data);
return ({some code});
}
When I ran this, I got null. Thank you so much in advance for helping me!
In React, Data flows in one direction only in form of props from Parent to Child.
Either move the logic to fetch params name to parent and pass it to child as props or have a central data storage such as redux and dispatch an action from child to save the information there and fetch it from there in Parent.
If you want to pass a child to parent component data then use usecontext or use redux to pass the data I recommend you can use redux.
parent component is handling the state
import GetUsername from "./GetUserName"
import { useState } from 'react'
const Test = () => {
const [param, getParam] = useState('');
console.log(param)
return (
<div>
<GetUsername getParam={getParam}/>
</div>
)
}
export default Test
and the children GetUserName utilizes the getParam useState setter to set the state handled in its parent component Test
import { useEffect } from "react";
import { useParams } from "react-router-dom";
const GetUsername = ({getParam}) => {
const params = useParams();
useEffect(()=>{
getParam(params)
},[])
return <p>children component</p>;
};
export default GetUsername
you will see that the console.log in the Test component will output your param received in its child component GetUserName.
in case you don't want to pass the props inline, you can use useContext, or redux, or any other state management library like zustand, recoil, etc...
You can pass the setState hook as a prop from the parent to the child and when you have access to the data. Use the setState hook.
Parent Component:
export default function App() {
const [name, setName] = useState();
return (
<div className="App">
<h1>Hello {name}</h1>
<Child setName={setName} />
</div>
);
}
Child Component:
export default function Child({ setName }) {
const [param, getParam] = useState("blabla");
useEffect(() => {
setName(param);
}, []);
return (
<>
<input onChange={(e) => setName(e.target.value)} />
</>
);
}

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 - What is meant by 'Do not use HOC’s in the render method of a component. Access the HOC outside the component definition.'?

I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)

update state of child component from parent component using react hooks

This is how my RespMessages component looks like:
import React, { useState } from "react";
import { Message } from "semantic-ui-react";
function RespMessages() {
const [message, setMessage] = useState("This is a success message");
return (
<Message positive>
<Message.Header>{message}</Message.Header>
</Message>
);
}
export default RespMessages;
This is how I am using RespMessages component inside a different component.
function CreateChannel() {
return (
<Container>
<RespMessages />
</Container>
}
This works fine and I can see This is a success message when the page renders.
What I am not able to figure out is how do I call setMessage of RespMessages component from CreateChannel component.
Can you please help?
P.S.: I am react newbie so jargons of react are probably off.
You can do it using useImperativeHandle. Here is an example.
let RespMessages = forwardRef((props, ref) => {
const [message, setMessage] = useState("This is a success message");
const inputRef = useRef();
useImperativeHandle(ref, () => ({
setMessage: (msg) => setMessage(msg)
}));
return <div>{message}</div>;
});
function App() {
// In order to gain access to the child component instance,
// you need to assign it to a `ref`, so we call `useRef()` to get one
const childRef = useRef();
return (
<div>
<RespMessages ref={childRef} />
<button onClick={() => childRef.current.setMessage('hey')}>Click</button>
</div>
);
}
As stated in the docs, this approach should be rarely used. Also consider approaches in other answers.
In react if you want something to change in the Child component depending on the Parent component you have to lift the state up to the parent component. If you want to differentiate between what should be state and what should be props and where should the state reside then its simple to figure out.
State: Keep the state (message) in the component where the data will change. In this case CreateChannel component.
Props: The components that are dependent on that state (message) should get the state as Props. In this case RespMessages component.
setState Methods: Now in case the child also wants to be able to set the state (message) then you can also pass the setMessage down as prop from CreateChannel to RespMessages`.
The react docs have a very good explanation for all this stuff. Please check it out if needed.
RespMessages.jsx
import React from "react";
import { Message } from "semantic-ui-react";
function RespMessages({message}) {
return (
<Message positive>
<Message.Header>{message}</Message.Header>
</Message>
);
}
export default RespMessages;
CreateChannel.jsx
import React, { useState } from "react"
import { Container } from "semantic-ui-react"
function CreateChannel() {
const [message, setMessage] = useState("")
return (
<Container>
<RespMessages message={message} />
</Container>
)
}
export default CreateChannel
You don't. A parent company generally should not know about the implementation of a child.
You can move your messages state into parent and pass the messages to child as props. This will change your RespMessage component from being an uncontrolled component, to a controlled component.

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} />

Resources