I have a component called home pages where navbar i using i like use this same navber with my another component called shop.js with different data
This is the home component
const Home = () => {
return (
<React.Fragment>
<Header/>
</React.Fragment>
);
};
This is the header component
const Header = () => {
return (
<header>
<TopModal/>
<Navbar/>
<HeroSlider/>
</header>
);
};
This is the Shop.js components
export default function Shop() {
return (
<React.Fragment>
{/*this component coming from home component*/}
<Header/>
</React.Fragment>
)
}
I want to change the navbar value in shop.js?
make a separate component of the navbar and add the navbar component in the app.js file which will add the navbar to all files or add the navbar component to other component files in which you want the navbar and use props to change the button or data of the navbar
You can use the children property of the Header component to pass your own components:
const Header = ({children}) => {
let navbar = Navbar
if (children.length) {
navbar = children[0]
}
return (
<header>
<TopModal/>
{navbar}
<HeroSlider/>
</header>
);
};
Your Home component will be the same, while your Shop instead:
export default function Shop() {
return (
<React.Fragment>
<Header>
<ShopNavbar/>
</Header>
</React.Fragment>
)
}
I'm new to React and having some difficulty trying to add a new child component to a component that has already been rendered.
I have an App component which initially contains a Main component (main menu).
I also have Popover components which I want to appear on top of Main when they are children of <App> (and hence siblings of <Main>).
These Popover components vary in number. Each <Popover> can contain buttons which launch another <Popover> over the top again. So the structure would be like
<App>
<Main></Main>
<Popover></Popover>
<Popover></Popover>
...
</App>
However, when the page first loads there are no Popover components open, and the<App> is rendered without any. Here is a stripped-down version of my code:
class App extends React.Component{
constructor(props){ super(props) }
render(){
return (
<div>{this.props.children}</div>
)
}
}
class Main extends React.Component{
constructor(props){ super(props) }
render(){
return (
//main menu stuff here
)
}
}
ReactDOM.render(<App><Main /></App>, root);
How can I add new <Popover>s to my <App> when the user clicks something? Before React I would simply do App.appendChild(Popover) kind of thing, but I'm quite lost here.
I should add that the elements the user will click to trigger an initial <Popover> are not contained within <Main>; they are outside of the <App>, as I am trying to slowly transition my existing page to using React. I think this could be part of my problem.
So basically in React, you have multiple ways of doing this, but to be more reliable you need to have data that represents the dynamic components you will render in your DOM. And to do this you need to create a state and a function that can add new information to your state. Then simply by sharing this function with your other components, you can trigger it from wherever you want, and this will update your state which will increase the amount of dynamic components you will render.
Take a look at this example
import { useState } from "react";
export default function App() {
const [popups, setPopups] = useState([]);
const addNewPopup = () => {
setPopups([...popups, { title: "I am a popup" }]);
};
return (
<div className="App">
<ChildComponent onClick={addNewPopup} />
{popups.map((p) => {
return <Popup title={p.title} />;
})}
</div>
);
}
function ChildComponent({ onClick }) {
return (
<div>
<p>I am a child component</p>
<button onClick={onClick}>Add new element</button>
</div>
);
}
function Popup({ title }) {
return <div>I am a popup with title = {title}</div>;
}
I want to render the footer component to all pages except the 404 page and request timeout page.
Should I add footer component to all page component individually?
Generally: No,
you should avoid repeating code,
in React you should use composition a lot (break the ui into a component-hierarchy).
Of course, if your App is very simple or has very few pages, it might be quicker and cleaner to just add the Footer where you want it.
You should have some kind of "page layout" component. (You probably already have one.)
Then you have different options to tell your app if the footer should be shown or not:
(A) Seperate layout components
You could use 2 (or 3) different specialized page layout components.
e.g:
// -- main layout --
export const MainPageLayout = (props) => {
return (<>
<PageHeader />
{ props.children }
</>);
};
// -- specialized layouts --
export const NormalPageLayout = (props) => {
return (<MainPageLayout>
{ props.children }
<PageFooter />
</MainPageLayout>);
};
export const ExceptionalPageLayout = (props) => {
return (<MainPageLayout>
{ props.children }
</MainPageLayout>);
};
// -- pages --
export const ExamplePage = (props) => {
return (
<NormalPageLayout>
<h1>example page</h1>
<div>example page content</div>
</NormalPageLayout>
);
};
export const Page404 = (props) => {
return (
<ExceptionalPageLayout>
<h1>404</h1>
<div>not found</div>
</ExceptionalPageLayout>
);
};
(B) use a prop
You could use the same page layout component with e.g. a "page type" property.
Even if this is shorter, I would generally not recommend this solution, because IMO it is less declarative, but I must admit that I have trouble to reason this.
e.g:
// -- layout --
export const MainPageLayout = (props) => {
return (<>
<PageHeader />
{ props.children }
{ props.pageType === 'exceptional'
? null
: <PageFooter />
}
</>);
};
// -- pages --
export const ExamplePage = (props) => {
return (
<MainPageLayout>
<h1>example page</h1>
<div>example page content</div>
</MainPageLayout>
);
};
export const Page404 = (props) => {
return (
<MainPageLayout pageType={ 'exceptional' }>
<h1>404</h1>
<div>not found</div>
</MainPageLayout>
);
};
In a website, I have multiple react rendering elements. I want to pass the data between this 2 individual elements. What are the possible options for passing the data between 2 elements
ReactDOM.render(<Header/>, document.getElementById('header'));
ReactDOM.render(<SideBar/>, document.getElementById('sidebar'));
I want to have a single data storage between these elements. Like I get data in one component and I want that data to be accessible in all the elements (in all ReactDOM).
So what can be the possible options for this?
Edit: Due to requirement I can't merge them in to same root component. Only some portion of page is in react other is still in the HTML/Jquery. So I render them individually in the required div.
Does redux store work between different ReactDOMs?
Use Portals from the react-dom library:
Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.
const HeaderPortal = ReactDOM.createPortal(<Header />, document.getElementById('header'))
const SideBarPortal = ReactDOM.createPortal(<SideBar />, document.getElementById('sidebar'))
const store = createStore(/* ... */)
ReactDOM.render(
<YourDataStoreProvider store={store}>
<HeaderPortal />
<SideBarPortal />
</YourDataStoreProvider>,
document.getElementById('root') // assuming you have an empty "dummy" node with that id
);
Just render you app into a container anywhere in your DOM or create a new "dummy" node. In my example I assume there is an empty node with the id root. Then render your other components into a portal.
Does redux store works between different ReactDOMs?
Your app will work as if it was rendered entirely into the same container. If you use Portals your components will be in the same component tree having the same context while being rendered somewhere else.
Should I use Portals?
Using Portals usually is intended to be used for components that visually need to "break out" of its container like modals or dialogs. But you can also use them to create widget-like components that can be rendered anywhere.
Creating a generic Portal component
You can also create a generic <Portal> component that creates a portal given a container id:
import {createPortal} from 'react-dom';
const Portal = ({children, container}) => createPortal(
children,
document.getElementById(container),
);
export default Portal;
And use it this way:
ReactDOM.render(
<YourDataStoreProvider store={store}>
<Portal container="header">
<Header />
</Portal>
<Portal container="sidebar">
<SideBar />
</Portal>
</YourDataStoreProvider>,
document.getElementById('root')
);
EDIT:
You need one node in the DOM where you can render your app. This can be a new DOM element that you create or it can be one of the containers you already have. Given you use the <Portal> component from above it could also look like this:
ReactDOM.render(
<YourDataStoreProvider store={store}>
<Header /> // this will be visible in your header container
<Portal container="sidebar">
<SideBar /> // this will be rendered to the sidebar container
</Portal>
</YourDataStoreProvider>,
document.getElementById('header')
);
This will render your app in the header container. But only your <Header> component will actually have a DOM representation in that container. The sidebar will be rendered in the sidebar container by the portal. But still they will share the same react component tree and have the same store provider.
You can create a component container and have both as a child and render them separate. Exchange the data through props and the mother container.
I provide an example:
https://codepen.io/hkares/pen/rvLPzQ
HTML
//html
<header id="header"></header>
<main>
<nav id="nav"></nav>
<section>I am section</section>
</main>
<footer>This is a footer</footer>
<div id="useless"></div>
Index.js
const HEADER_NODE = document.querySelector("#header");
const NAVBAR_NODE = document.querySelector("#nav");
const Header = ({ name }) => ReactDOM.createPortal(<h1>{name}</h1>, HEADER_NODE);
const NavBar = (props) => ReactDOM.createPortal(
<article>
<label>
Set header name: <input {...props} />
</label>
</article>
, NAVBAR_NODE);
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "this is a header"
};
}
onChange = ({ target }) => this.setState({ value: target.value });
render() {
const { value } = this.state;
return (
<React.Fragment>
<Header name={value} />
<NavBar value={value} onChange={this.onChange} />
</React.Fragment>
);
}
}
ReactDOM.render(
<Container />,
document.querySelector("#useless")
);
container component
class App extends React.Component{
render(){
var data=[];
return(<span>
<Header data={data}/>
<SideBar data={data}/>
</span>);
}
}
Depends on the relationship between the two components.
Scenario I:If they are just siblings, then you can wrap them in your container component or just a simple , and passing the props to the child, like
<div>
<HeaderPortal data={yourSharedData}/>
<SideBarPortal data={yourSharedData}/>
</div>
or
<YourContainer>
<HeaderPortal />
<SideBarPortal/>
</YourContainer>
Then in YourContainer, you can define your shared data, and add them to each of your child like
render(){
<div>
{children.map(child=> React.cloneElement(child, {
data: yourSharedData
})}
</div>
}
Scenario II If these two components are far from each other
Like the above, you wanna share data between 2 and 12, then you should use redux storage to share the data, which is a central places to manage your component state, and share any part it to the required component via mapStateToProps, you can find more details here.
Build a context system:
a context is an object that stores data.
a module can access and update data in context.
a module can be notified when data in context is updated.
depends on your need, a module can either freely add data to context, or restrict to only update existing data.
It's not really a react specific problem.
An example of context system look like this:
content.js
import uniqueId from 'lodash/uniqueId';
let context = {
foo: '',
bar: ''
};
let listeners = {
// key: [{ id, callback }]
};
let getAppContext = function(key) {
return context[key];
};
let updateAppContext = function(key, value) {
if (!context.hasOwnProperty(key)) {
throw new Error(
`Failed to update appContenxt: "${key}" does not exist.`
);
}
let oldValue = context[key];
context[key] = value;
let callbacks = listeners[key];
if (callbacks !== undefined && callbacks.length !== 0) {
callbacks.forEach(function(item) {
item.callback(value, oldValue);
});
}
};
let onAppContextUpdate = function(key, callback) {
if (!context.hasOwnProperty(key)) {
throw new Error(
`Failed to add listener on appContenxt update: "${key}" does not exist.`
);
}
if (listeners[key] === undefined) {
listeners[key] = [];
}
let isListening = true;
let id = uniqueId();
listeners[key].push({ id, callback });
return function removeListener() {
if (isListening) {
listeners[key] = listeners[key].filter(i => i.id !== id);
isListening = false;
}
};
};
export {
getAppContext,
updateAppContext,
onAppContextUpdate,
};
Usage
import { getAppContext, updateAppContext, onAppContextUpdate } from 'content.js';
// read foo
let foo = getAppContext('foo');
// update foo
updateAppContext('foo', 123);
// get notified when foo is updated
let removeListener = onAppContextUpdate('foo', function(newVal, oldVal) {
console.log(newVal, oldVal);
});
// unlisten
removeListener();
I am having a hard time figuring out how to mount components inside a nested components with react router v1.0. I have an App component that loads a Layout component. The Layout component then loads two components, Menu and Content. I want to load different components inside the Content component based on the route.
Below is my sample code.
var App = React.createClass({
render : function(){
return <div><Layout/></div>
}
});
var Layout = React.createClass({
render : function(){
return(
<div>
<Menu/>
<Content/>
</div>
)
}
});
var Content = React.createClass({
render : function(){
return <div>This is where i want to mount my components</div>
}
});
var List = React.createClass({
render : function(){
return <div>some list goes here</div>
}
});
var Graph = React.createClass({
render : function(){
return <div>some graph goes here</div>
}
});
<Router>
<Route path='/' component={App}>
<Route path='/list' component={List}/>
<Route path='/graph' component={Graph}/>
</Route>
</Router>
Any help/pointers will be highly appreciated.
Thanks
It's all the same as basic React components. When you nest them, they're available on this.props.children. So you would end up with something like this:
var App = React.createClass({
render : function(){
return <div><Layout>{this.props.children}</Layout></div>
}
});
var Layout = React.createClass({
render : function(){
return(
<div>
<Menu/>
<Content>{this.props.children}</Content>
</div>
)
}
});
var Content = React.createClass({
render : function(){
return <div>{this.props.children}</div>
}
});