Call Parent function from route this.props.children in React - reactjs

I have below layout and in this layout Route pages are being rendered.
const Layout = () => {
return (
<main className="fadeIn animated">
{ this.props.children }
</main>
)
}
return (
<React.Fragment>
<Layout callFunction={ () => console.log('callFunction') }/>
{/*I will set the state using callFunction Function and then pass in modal*/}
<Modals data={this.state}/>
</React.Fragment>
)
The page that is being rendered inside the children using route is below
class Page extends React.Component {
return (
<React.Fragment>
ROUTE PAGE IS . I would like to call callFunction from here
<button onClick={parent.callFunction}>CALL</button>
</React.Fragment>
)
}
I would like to call callFunction function of Layout Component from Children Page in reactjs

If i understand you correctly, you wish "Layout" to run the "loadLayout" function
if so, just let the Layout know what it's receiving
const Layout = ({loadLayout}) => {
loadLayout()
return (
<main className="fadeIn animated">
{ this.props.children }
</main>
)
}
I'm not sure this is what you meant, we need more info, who loads who

Related

Change the state of another component using functionnal components in React

I have a button in my header that has to switch the state of the lateral menu of the page (to know if it has to be shown or not). I found out on Internet how to do so using Class Components, but not with Functional Components, do you have any idea on how to achieve that ?
Here's a simplification of my actual code in order to reproduce my issue (I removed all useless code).
App.js :
function App() {
return (
<div className="App">
<Header />
<div>
<LateralMenu />
</div>
</div>
);
}
The Header component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
function changeLateralMenu() {
setLateralIsOpen(!lateralIsOpen);
}
return (
<header>
<div onClick={ changeLateralMenu }>
</header>
);
}
And the LateralMenu component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<section>
{ lateralIsOpen ? "open" : "closed" }
</section>
);
}
I tried (but maybe not correctly) to declare the lateralIsOpen State in the App component and sending it through props to my children componenents (Header & LateralMenu).
I also tried looking at this question (and a few others) which is pretty similar, but don't see how I can apply it in my case because (as I understand) he uses a button in the parent component, that changes a state in the parent component, and then send it to the children through props... Where in my case, the button to switch it is already in a child.
I'd suggest you to move the state out of the LateralMenu to the parent (App) and then just pass the toggle function to the Header to toggle it.
export default function App() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<div className="App">
<Header toggleLateral={() => setLateralIsOpen((prev) => !prev)} />
<div>
<LateralMenu isOpen={lateralIsOpen} />
</div>
</div>
);
}
function Header({ toggleLateral }) {
function changeLateralMenu() {
toggleLateral();
}
return (
<header>
<div onClick={changeLateralMenu}>click</div>
</header>
);
}
function LateralMenu({ isOpen }) {
return <section>lateral is {isOpen ? 'open' : 'closed'}</section>;
}
Codesandbox: https://codesandbox.io/s/eager-heyrovsky-z75njd

How to re-render children in ReactJS?

Problem Context
I have a component named <Layout/> which contains my header and footer. It wraps all my routes as such:
<Layout>
<div className="container">
<Route path="/sign-in" exact component={SignIn}/>
<ProtectedRoute path="/" exact component={Home}/>
<ProtectedRoute path="/statistics" exact component={Statistics}/>
...
...
</div>
</Layout>
My <Layout/> component is defined as such:
const Layout = (props) => {
return (
<div>
<Header/>
{props.children}
<Footer/>
</div>
);
}
I did this so that i don't have to include my header and footer in each and every component.
Problem
In my header component, I am using the instance auth which indicates whether a user is logged in or not. The auth changes after the user signs in. However, even though the auth changes, my <Header/> component in the <Layout/> is not re-rendered. I have to manually refresh it to incorporate the changes.
My <Header/> component is defined as:
import auth from '../../auth';
const Header = () => {
return (
{auth.isAuth() ?
//render icon A
: null
}
<div>
Healthcare Management System //header title
</div>
{auth.isAuth() ?
//render icon B
: null
}
</div>
);
}
export default Header;
This is my auth class:
class Auth{
constructor(){
this.authenticated = JSON.parse(sessionStorage.getItem('profile'));
}
login(cb){
this.authenticated = JSON.parse(sessionStorage.getItem('profile'));
cb();
}
logout(cb){
this.authenticated = null;
cb();
}
isAuth(){
return this.authenticated;
}
}
export default new Auth();
What I require
What i want is supposedly simple; when the auth.isAuth() == null, show no icons and only the title (this behaves correctly). When, the auth.isAuth() != null, show icons A and B (this does not behave correctly and requires me to refresh the page in order to render the icons).
Somehow, i want the <Layout/> component to re-render once the auth changes. Thank you!
React component is only rerendered when its props or its state is changing, therefore you should put auth in a state which can be set as followed:
import auth from '../../auth';
const Layout = (props) => {
const [authenticated, setAuthenticated] = React.useState(false);
React.useEffect(() => {
if(!auth.isAuth()) {
setAuthenticated(false);
} else {
setAuthenticated(true);
}
}, []); // this useEffect logic is not working, you need to determine how you want to configure the condition in which authenticated state is set
return (
<div>
<Header authenticated={authenticated} />
{props.children}
<Footer/>
</div>
);
}
Header component:
const Header = ({ authenticated }) => {
return (
{authenticated ?
//render icon A
: null
}
<div>
Healthcare Management System //header title
</div>
{authenticated ?
//render icon B
: null
}
</div>
);
}
export default Header;

Creating a parent 'workspace' component in ReactJS

Using ReactJS, I am trying to create a common workspace component that will have toolbar buttons and a navigation menu. The idea I have is to re-use this component to wrap all other dynamic components that I render in the app.
Currently, I've created a Toolbar and MenuBar components that I then add to each component in the app as such:
<Toolbar/>
<MenuBar/>
<Vendors/>
This does not feel right, since my aim is to have just one component which would be something like:
<Workspace>
<Vendor/>
</Workspace>
However, I am not sure of how to achieve this and whether this is the right approach.
As to whether or not it is the right approach is subjective, but I can provide insight into one way to make a "wrapper" type component:
// Your workspace wrapper component
class Workspace {
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
Toolbar goes here
</div>
<div className="workspace__nav">
Navgoes here
</div>
<div className="workspace__content">
{this.props.children}
</div>
</div>
)
}
}
// Using the component to define another one
class MyComponent {
render() {
return (
<Workspace>
This is my workspace content.
</Workspace>
)
}
}
You can also look at HOC's or Higher Order Components to wrap things.
React offer two traditional ways to make your component re useable
1- High-order Components
you can separate the logic in withWorkspace and then give it a component to apply that logic into it.
function withWorkSpace(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
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} />;
}
};
}
const Component = () => {
const Content = withWorkSpace(<SomeOtherComponent />)
return <Content />
}
2- Render Props
or you can use function props then give the parent state as arguments, just in case you need the parent state in child component.
const Workspace = () => {
state = {}
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
{this.props.renderTollbar(this.state)}
</div>
<div className="workspace__nav">
{this.props.renderNavigation(this.state)}
</div>
<div className="workspace__content">
{this.props.children(this.state)}
</div>
</div>
)
}
}
const Toolbar = (props) => {
return <div>Toolbar</div>
}
const Navigation = (props) => {
return <div>Toolbar</div>
}
class Component = () => {
return (
<Workspace
renderNavigation={(WorkspaceState) => <Navigation WorkspaceState={WorkspaceState} />}
renderTollbar={(WorkspaceState) => <Toolbar {...WorkspaceState} />}
>
{(WorkspaceState) => <SomeComponentForContent />}
</Workspace>
)
}

Adding a Link as a child of a Router with ReactDOM.render yields "You should not use <Link> outside a <Router>"

I am looking for a way to use ReactDOM.render to create a Link within a react router. The setup more or less looks like this:
const router = (
<div>
<Router>
<Route path="/map" component={Map}/>
</Router>
</div>
);
The relevant parts of Map.jsx look like this:
const MapPopup = () => {
return (
<Link to={`/map/add`} />
)
}
class Map extends React.Component {
componentDidMount() {
this.map = L.map('map')
//...stuff...
this.map.on('contextmenu', event => {
popup
.setLatLng(event.latlng)
.addTo(this.map)
.setContent(
ReactDOM.render(
MapPopup(),
document.querySelector('.leaflet-popup-content')
)[0]
)
.openOn(this.map)
})
}
render() {
return (
<React.Fragment>
<div id="map" />
</React.Fragment>
)
}
}
I am basically trying to add a Link to the map popup provided by leaflet (I can't use react-leaflet for this project). If I however return the MapPopup directly in the render function it works (obviously not in the popup but the Link does work this way).
<React.Fragment>
<div id="map" />
<MapPopup />
</React.Fragment>
Does anyone have an idea how I can tackle this rather unusual problem?
I am using "react-router-dom": "4.3.1".
This is the expected error since <Link> component expects ancestor component to be of router type (<BrowserRouter>, <MemoryRouter>, <Router> ... ), refer this thread for a more details.
For your scenario to circumvent this limitation ReactDOM.createPortal could be utilized instead of ReactDOM.render:
<Route
path="/popup"
render={() => (
<Popup>
<div>
Some content goes here
<Link to="/map"> Back to map</Link>
</div>
</Popup>
)}
/>
where
class Popup extends React.Component {
render() {
return ReactDOM.createPortal(
this.props.children,
document.querySelector("#link-render-div")
);
}
}
and
Here is a demo for your reference

Pass component tag as a props in react native

It could be a silly question but I was wondering is there any way to pass a component as props to component tag.something like this
const Main = (props) => {
return (
<header>
main
</header>
<Content>
<{props.homePage} /> //this should be like this <Home />
</Content>
<Footer>
</Footer>
);
};
I have a main page that contains header, content, and footer and I want to change dynamically the component in the content.but whenever I pass the component as props react take it as a raw text and give me an error. Please, tell me if this method is the correct way or if I have to use another way.
You were very very close, it should look something like:
const Page1 = (props) => <div>my first page</div>;
const Main = (props) => <div>{props.content}</div>;
class App extends React.Component {
render () {
return (
<div>
Header
<Main content={<Page1 />} />
Footer
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
http://codepen.io/cjke/pen/VPYJpK?editors=0010
As an alternative, and probably a bit more natural is to leverage children:
const Page1 = (props) => <div>my first page</div>;
const Main = (props) => <div>{props.children}</div>;
class App extends React.Component {
render () {
return (
<div>
Header
<Main><Page1 /></Main>
Footer
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));

Resources