Passing props to a react component wrapped in withRouter() function - javascript

I am using React-Router v4 to navigate in my React app. The following is a component wrapped in the withRouter() function to make it able to change route on click:
const LogoName = withRouter(({history, props}) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));
As you can see I pass the props to the component, which I need in order to change the class of the component. The problem here is that props is undefined in the <LogoName> component. I need to be able to change the class of this component when I click on another component, like this:
<LogoName className={this.state.searchOpen ? "hidden" : ""} />
<div id="search-container">
<SearchIcon
onClick={this.handleClick}
className={this.state.searchOpen ? "active" : ""} />
<SearchBar className={this.state.searchOpen ? "active" : ""}/>
</div>
Here is how I handle the click. Basically just setting the state.
constructor(){
super();
this.state = {
searchOpen: false
}
}
handleClick = () => {
this.setState( {searchOpen: !this.state.searchOpen} );
}
Is there a way for me to pass props to a component that is wrapped inside the withRouter() function or is there a similar way to create a component which has the ability to navigate with React-Router and still receive props?
Thanks in advance.

The problem is that while destructuring, you want to destructure props but you are not passing any prop named props to LogoName component
You can change your argument to
const LogoName = withRouter((props) => (
<h1
{...props}
onClick={() => {props.history.push('/')}}>
BandMate
</h1>
));
However you can still destructure the props like #Danny also suggested by using the spread operator syntax like
const LogoName = withRouter(({history, ...props}) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));

You're close, just spread the props in your function signature as well:
const LogoName = withRouter(({ history, ...props }) => (
<h1
{...props}
onClick={() => {history.push('/')}}>
BandMate
</h1>
));

This worked for me:
import {withRouter} from 'react-router-dom';
class Login extends React.Component
{
handleClick=()=>{
this.props.history.push('/page');
}
render()
{
return(
<div>
.......
<button onClick={this.handleClick()}>Redirect</button>
</div>);
}
}
export default withRouter(({history})=>{
const classes = useStyles();
return (
<Login history={history} classes={classes} />
)
});

Related

How to clone components with usestate hook, so they will change independent from each other?

const Section = ({ text, changeFn }) => {
return(
<>
<Content text={text} changeFn={changeFn}/>
<Content text={text} changeFn={changeFn}/>
<Content text={text} changeFn={changeFn}/>
</>
)
}
const Content = ({ changeFn, text }) => {
const [ textCont, setText ] =useState('text')
return(
<>
<div>{textCont}</div>
<button onClick={changeFn}>Edit</button>
</>
)
}
const Form = ({ changeFn, setContent }) => {
let inputRef = useRef()
const applyChangefn = () => {
setContent(inputRef.current.value)
changeFn()
}
return(
<>
<input ref={inputRef}/>
<button onClick={applyChangefn}>Save</button>
</>
)
}
I want to make a component which exists as example with its own usestate in Content component.
Then i want to send it multiplied by 3 times into Section component.
First question is how to send props between sibling components
Second question is how to make 3 same components which have its own useState state. I mean 3 same components with independent usestate.

withSyles HOC does not pass innerRef along with other props when passing through React.forwardRef

I'm passing the ref using React.forwardRef to the down component which usually works.
<SomeComponent
component={React.forwardRef((props, ref) => <MyComponent innerRef={ref} {...props} />)}
.../>
However when I have HOC (higher order component) withStyles, the innerRef along with other props do not get passed properly.
// innerRef does not exists in props
const MyComponent = withStyles(styles)(({ one, two, ...props }) => {
return (
<Fragment>
<NavLink {...props}></NavLink>
...
</Fragment>
);
})
Without using withStyles I get them perfectly
// innerRef exists in props
const MyComponent = ({ one, two, ...props }) => {
return (
<Fragment>
<NavLink {...props}></NavLink>
...
</Fragment>
);
}
How can I still have the withStyles HOC but with innerRef and other props included?
The issue appeared after migration from material ui v3 to v4. NavLink requires the innerRef property.
withStyles passes along innerRef as ref, so something like the following should work:
const MyComponent = withStyles(styles)(({ one, two, ...props }, ref) => {
return (
<Fragment>
<NavLink {...props} ref={ref}></NavLink>
...
</Fragment>
);
})
Or if you need to keep it as innerRef on NavLink:
const MyComponent = withStyles(styles)(({ one, two, ...props }, ref) => {
return (
<Fragment>
<NavLink {...props} innerRef={ref}></NavLink>
...
</Fragment>
);
})
My recommendation based on my comments:
<SomeComponent
component={React.forwardRef((props, ref) => <MyComponent nRef={ref} {...props} />)}
.../>
const MyComponent = withStyles(styles)(({ one, two, ...props }) => {
props.innerRef = nRef;
return (
<Fragment>
<NavLink {...props}></NavLink>
...
</Fragment>
);
})
Or alternatively
<NavLink {...props} innerRef={props.nRef}></NavLink>

How do i display Breadcrumb name conditionally on response from child.in React with hooks?

I am currently working on a project with React Hooks.
Parent component is a Navigator
Child component is a breadcrumb display in this navigator.
Child component fetches and displays a view with the data.
How can i use the response data in the 2. child component to set name in the 1. Child component?
My Code (omitted large portions of unnecessary code for this example):
Navigator
const { Header, Content } = Layout;
const Navigation = (props: any) => (
<>
<Layout>
<Layout>
<Header>
<Breadcrumbs
style={{ flexGrow: 2, paddingLeft: 20 }}
name='Name of User'
/>
</Header>
<Content style={{ margin: '24px 16px 0', overflow: 'hidden' }}>
<div className="content">
<Switch>
<Route exact path="/" component={MyPatients} />
<Route exact path="/Skjema" component={MySchemas} />
<Route
exact
path="/Pasient"
component={() =>
<PatientInfo
patientID={props.history.location.state}
/>
}
/>
export default withRouter(Navigation);
BreadCrumbs
import React from 'react';
import Breadcrumb from 'antd/lib/breadcrumb';
import { HomeOutlined, UserOutlined } from '#ant-design/icons';
const Breadcrumbs = (props: any) => {
return (
<>
<div className="Breadcrumbcontainer" style={props.style}>
<Breadcrumb>
<Breadcrumb.Item href="/">
<HomeOutlined />
<span style={{ color: 'black' }}>Hjem</span>
</Breadcrumb.Item>
<Breadcrumb.Item href="Pasient">
<UserOutlined />
<span style={{ color: 'black' }}>
{props.name}
</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>Skjema 1 - 17.04.20</span>
</Breadcrumb.Item>
</Breadcrumb>
</div>
</>
);
};
export default Breadcrumbs;
The third file contains a fetch to an api and works fine, the data in question is currently stored as response.name How can i lift this info up to Navigator?
If I understood your question correctly, there's a parent component that has two child components and you want to trigger a change from one child component in another.
You can maintain the state in the parent component, pass state in child1 and setState function in child2.
// Parent Component
const [name, setName] = useState('');
<>
<child1 name={name}/>
<child2 setName={setName}/>
</>
Try this one. I added code sandbox and you can check it out is this what you need. So from parent pass hook as props to a child, and then after the request is made inside of the child component call function from props that will fill the data inside parent component.
Update state in parent from child
I found a solution thanks to the suggestions in this thread. I made my mistake in the passing of the files to the function.
const PatientInfo = ({ patientID, setName }: any) => {
console.log(useFetch<IPatient>( // name of endpoint.... ));
const { response } = useFetch<IPatient>(
'// name of endpoint.... + patientID,
patientID,
);
This ended up fixing my problem. The problem was i initially called the two seperately, like this:
const PatientInfo = ({ patientID }: any, { setName } : any) => {
console.log(useFetch<IPatient>( // name of endpoint.... ));
const { response } = useFetch<IPatient>(
'// name of endpoint.... + patientID,
patientID,
);
So this change worked, although I am not entirely sure as to why.

react - How to open show more on a function component?

I have a functional component
const text = ({data}) => {
return (
<p onClick={()=> render more?}>info</p>
)}
const more = ({data}) => {
return (<p>..........</p>)
}
Is it possible to render more component on the onClick event?
Sure, you'll need a state variable. Use the state to determine whether to render more or not, and then set the state when the click happens. If you have react 16.8 or later, you can do this in a functional component with hooks:
import { useState } from 'react';
const MyComponent = ({data}) => {
const [showMore, setShowMore] = useState(false);
return (
<div>
<p onClick={() => setShowMore(true)}>info</p>
{showMore && <More data={data} />}
</div>
)}
}
Prior to 16.8, you'll need to use a class component.
class MyComponent extends React.Component {
state = {
showMore: false,
}
render() {
return (
<div>
<p onClick={() => this.setState({ showMore: true})}>info</p>
{this.state.showMore && <More data={this.props.data} />}
</div>
)}
}
}

How to forward ref in a HOC in React?

I am using Context API to use themes in my React Native project. To consume the Theme Context, I made a Higher Order Component and passed them into the component as props.
This worked fine for the most of the app. But when I started using refs, it started crashing, because as per the documentation, the refs will not be forwarded automatically, and we need to use the React.forwardRef API. But something when wrong with my implementation and I am not able to resolve it.
Here's the code I have been working on:
// Higher order component, to wrap my component with theme (withTheme.js)
export const withTheme = (Component, areRefsUsed = false) => {
// Logic to check if refs are being used
if (areRefsUsed) {
const ThemedComponent = (props, ref) => {
return (
<ThemeContext.Consumer>
{theme => (<Component {...props} theme={theme} ref={ref} />)}
</ThemeContext.Consumer>
)
};
return React.forwardRef(ThemedComponent);
}
return (props) => {
return (
<ThemeContext.Consumer>
{theme => <Component {...props} theme={theme} />}
</ThemeContext.Consumer>
)
}
};
// My component where I am using refs (ExampleComponent.js)
class ExampleComponent extends Component {
constructor(props) {
super(props);
this.refForSomeComponent = React.createRef();
}
render() {
const { theme } = this.props;
return (
<Fragment>
<Text style={{color: theme.primaryColor}}>It is working</Text>
<TextInput ref={this.refForSomeComponent} value={'Test'} />
</Fragment>
)
}
}
export default withTheme(ExampleComponent, true);
When I try to run the app, this error is being thrown:
The component for route 'ExampleComponent' must be a React component.

Categories