I have the following Parent Component that contains several Child components:
class Parent extends React.Component {
render() {
<div>
<Child id='1'/>
<Child id='2'/>
<Child id='3'/>
<Child id='4'/>
<Child id='5'/>
<Child id='6'/>
<Child id='7'/>
<Child id='8'/>
</div>
}
}
I want to have pagination in the Parent class that shows maximum 3 Child components per 'page'. Is there a way to do that without a complex backend integration?
Here's a small demo which shows how you can accomplish something like that. It works by keeping a single variable in state for the current page being viewed, and rendering elements conditionally depending on whichever is the current page. You can add onto this with individual links that set the page directly, or whatever you prefer.
For anything more complex, look into routing, which is the concept of syncing your app state to the URL bar, to enable the user to use back/forward buttons in the browser for navigation. React Router is one of many libraries that can be used to accomplish this.
use a state variable like current_page_no which you can update from buttons like prev, next. Then just display 3 children starting at current_page_no*3.
Related
I have a question about React, here's a simplified version of a React app.
In the app I want to render a fixed primary menu and a secondary menu that is optional and its content is controlled by inner components rendered in routing.
Also secondary menu is rendered somewhere else in mobile version of the app.
function App() {
return <Router>
<PrimaryMenu/>
<SecondaryMenu/>
<LayoutContent/>
{/* This block is rendered only on mobile devices */}
<Responsive {...Responsive.onlyMobile}>
<SecondaryMenu/>
</Responsive>
</Router>;
}
LayoutContent will render actual page content (using a Page component) according to routing rules and every page component may render its own secondary menu like this (e.g. page1 has its own submenu, page2 has another one, page3 has not.)
<Page title='Page 1 - With secondary menu'>
<SecondaryMenuItems>
{/* I want this content as children of secondary menu in both mobile and desktop menubars */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenuItems>
</Page>
I tried to implement it by using React Contexts but if I store children nodes in context an infinite render is triggered. I changed it to use a id property in <SecondaryMenuItems/> component but the approach is very fragile and also has some drawbacks.
Here's my working example it's working but as I said is pretty fragile:
What if I use a duplicate id for secondary menus?
What if I forget a secondary menu key?
Also if you switch to a page with a menu and then go to page3 (that has no menu) previous page menu remain on screeen.
How to accomplish this with react? Is there a suggested way to do that?
A simpler way to express my question is "how to pass a set of react nodes between unrelated components (e.g. siblings components)"
Update
I've completed my working example with received hints, now by combining useRef with ReactDOM.createPortal I achieved final result which is now in the example.
This is a use case for React Portals. Portal will let you render secondary menu items from a page into secondary menu container that exists somewhere else
All you need to do is to call React.createPortal in render of thepage, pass rendered element and target node to render into, regardless of position in DOM tree
I've edited your example using portals here https://codesandbox.io/s/secondary-menu-example-vbm3x. This of course is a basic example, you might want to abstract portals logic in a separate component for convenience, and/or pass dom reference from parent, instead of calling getElementById on mount
Rendering same children in multiple sibling nodes
The question asks "how to pass a set of react nodes". Ideally, don't. If you are rendering nodes somewhere in your hierarchy with the intention of using them elsewhere, you may be using the wrong strategy.
If you need to render the same components in different places, make a function that renders the components, and call it from both places. In other words, always pass the information, not the rendered elements.
Render inside the router
In a typical Single Page Application, the router will render all of the (non-static) components. This is how the example should have done it. The routing component (LayoutContent) should have been responsible for rendering the "passed nodes" (SecondaryMenu) directly.
<Route path="/page1">
<Page title="Page 1 - With secondary menu">
<SecondaryMenu id="menu1"> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenu> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
</Page>
</Route>
When rendering inside the router is impossible
If for some reason the routing component cannot render the content directly, then a Single Page Application (or routing) solution is probably the wrong solution here. The question doesn't include any information as to why the components can't be rendered inside the router, feel free to edit the question and comment with more info.
Another way of achieving the example would be for there to be no routing component (i.e. no LayoutContent) and for SecondaryMenu to check the path of the page and conditionally render the appropriate content based on that.
It may seem silly to manually render conditionally based on a path when there is a router component which does this for you, and I would agree. The solution is then to not use a router at all. Trying to render children in the router and passing them has a strong code smell.
In the React hierarchical layout, if the same information is needed make decisions about rendering in multiple places (the path in this case), move that information up to the nearest parent of all components and pass it down as props or as context.
Avoiding ID clashes
"What if I use a duplicate id for secondary menus?"
If you call a function to render the secondary menu instead of rendering it and passing it, then you can pass a menu prefix in the props, and use this menu prefix in the function.
function SecondaryMenuItems({ children, idPrefix, path }) {
if (path == '/path1') {
return (
<ul id={`${idPrefix}-newlist`}>
On keys
"What if I forget a secondary menu key?"
React keys need only be unique within a rendered list. In fact, keys are simply an optimisation to prevent React having to re-render a generated list on every pass. If you forget to include a key (or make a bad choice of key), React has to re-render the list every time, but it's not more serious than that. In simple cases, you won't notice the drop in performance.
I'm using React 16.2.0, having trouble doing something it seems should be doable, but I'm new to React, so needing a sanity check here.
The app is a log viewer which fetches log entries from a backend database. I'm wanting to have a Paging component which maintains a log data pool, fetches more logs when the pool gets low, and on request grabs the next chunk to be displayed (dataChunk below).
I'm returning dataChunk in the render() method of the Paging component. I also have a simple View component that just renders a chunk of log entries contained in dataChunk.
As seen below, I'm wanting to have the dataChunk rendered/returned from Paging component passed as an attribute to the View component:
render() {
return(
<View dataChunk={<Paging chunkCb={this.chunkCb} state={pagingState}}/>
);
}
What is actually showing up in this.props.dataChunk in View is a react.element object (basically the Paging object). Paging is never instantiated/mounted.
Is what I'm wanting possible in React? Am I doing something wrong?
Thanks in advance.
First of all, you need to close the Paging tag properly:
render() {
return(
<View dataChunk={<Paging chunkCb={this.chunkCb} state={pagingState} />}/>
);
}
However, instead of passing a Component as a prop, just compose Paging inside the render() function of View component definition, unless View is a third-party library that uses Render Props pattern.
I have read many articles to find out the real time use case of this.props.children but i didn't find the answer that i am looking for.I know that this.props.children is used to access the data b/w the opening and closing tag of a component. But my question is why can't we add a prop to the component instead of writing data b/w opening and closing tag.
for Ex:
<Example>This is data<Example> //can be accessed as this.props.children
can be written as
<Example data="This is data"/> //can be accessed as this.props.data
Can somebody please explain me with a real-time example of where we can achieve a certain task by using only this.props.children?
For example if you have complicated children of a component:
<Card>
<div class='title'>Title</div>
<div class='content'>Content</div>
</Card>
It would be easier than if you write like:
<Card content={[<div class='title'>Title</div>, <....>]} />
Samething you can find here, for example in Drawer component of Material-UI here. Drawer is a component that slides from the left, it can contain anything, so using props.childrens.
While making an app, you want a parent component which will render anything in your component. The use cases which I can think of are:
When you want to open a different component depending upon the route change.
const App = ({ children }) => (
<div className="full-height">
{children}
</div>
);
When you want to have same styles throughout your app for generic elements such as body, head etc. You'll just have to apply on this component, e.g., in above example, the full-height will get applied everywhere in the app on top component. (Obviously there are other work arounds but this is always more clear)
For use cases where you want to expose your component (when component doesn't know children ahead of time) as libraries and props can vary a lot and complicates the rendering. Read this
Obviously you don't have to use it but it makes code more elegant and understandable.
The situation I have is a login screen that displays one of 3 blocks of code, depending on the store state of the app. Eg... Below would be the resulting render if the second display option was selected.
<LoginFormPage>
<DisplayOption2 />
</LoginFormPage>
There are specific calls and logic for each display option distinct enough to warrant their own containers. My file structure is:
/components
/displayOpt1.jsx
/displayOpt2.jsx
/displayOpt3.jsx
/loginFormPage.jsx
/containers
/displayOpt1.js
/displayOpt2.js
/displayOpt3.js
/loginFormPage.js
I need a way to render the correct option without embedding too much logic into the parent container; since it really doesn't need to know anything about any of the login mechanisms. I can think of a few ways to do it.
All logic in loginFormPage.js with a connect direct to the loginFormPage.jsx. Then conditional parameters in the loginFormPage.jsx that makes calls to the components directly; removing the other containers.
Create a React.Component in the loginFormPage.js to do the conditional rendering calls to the other containers; this would call all the .jsx files from the container component. loginFormPage.jsx would then render the selected child with {props.children}.
Same as 2. but do the conditional rendering call in the mergeProps parameter passed to connect in loginFormPage.js; rather than creating a jsx component in the container js code.
Some standard practice that I don't know of?
Right now I'm leaning towards option 3, but I can't find any evidence with my Google searches of this being a recommended practice. All thoughts are welcome. Thanks.
Some code to maybe make it easier:
loginFormPage.jsx
<div>
<div onClick={props.someActionHeader}>
<h1>Login Form</h1>
</div>
<div className="formarea">
// render the selected option here based on props.renderOptionChoice
// this will be one of displayOpt1, displayOpt2, displayOpt3
</div>
<div className="otherstuff">...</div>
</div>
displayOpt1.jsx - Opt2.jsx and Opt3.jsx code is a variation of something like this
<div onClick={props.someAction1}>
stuff...
</div>
loginFormPage.js
import LoginFormPage from '../components/loginFormPage'
const mapStateToProps = (state, ownProps) => {
return {
renderOptionChoice: state.login.selectedLoginType,
}
}
const mapDispatchToProps = ...
export default connect(mapStateToProps, mapDispatchToProps)(LoginFormPage)
I can answer with what I've found to be the best practice. It's worth reading the 3 posts in my comment to the question.
The Container level should contain the What is being displayed. In terms of a Login screen with several different options, then all the What's should be presented in one file. Following this style makes it clear exactly What is being displayed on the particular screen / component simply by looking at a single file.
So at the top level, the render will look like:
render() {
return (
<LoginPage>
{this.state.step === STEPS.Step1 && <LoginStep1 />}
{this.state.step === STEPS.Step2 && <LoginStep2 />}
{this.state.step === STEPS.Step3 && <LoginStep3 />}
</LoginPage>
)
}
Here LoginStep1/2/3 can be contained components with their own connection to the Redux state, or it can be managed at the LoginPage level if the steps code is very simple or strongly related.
I'm working to build a signup > profile setup flow for a new application which is using React + Redux.
I found the following example for a redux form wizard:
http://redux-form.com/6.7.0/examples/wizard/
This seems to be the closest example but the problem is each step in the wizard does not change the URL, so if the user clicks the browser's forward/back btn or refreshes their browser, it will break.
Would it be OK to make Redux form wizard have permanent URLs? How can I approach this as a beginner?
Something where the wizard has URLs like:
/wizard/name
/wizard/profile
/wizard/photo
Would it be OK to make Redux form wizard have permanent URLs?
The way how it's implemented depends on every use case. I don't see any problem why it wouldn't be ok.
The wizard example tells you everything you need to know regarding your approach.
Talking about the official example, you could approach this in the following way:
Create a /wizard/name route which would render the same WizardForm.js component;
If the subpath is /name, render the WizardFormFirstPage
Same for the other subpaths.
I recently implemented a redux-form wizard with client-side routing. I will share some of the details here for posterity...
I have a component called MainContent which has several React-Router 4 routes. One of these routes looks like this:
<Route path={'/profile/:profileSection'} component={Profile} />.
This route loads a "wizard" style redux-form that the user fills out to set up her profile.
Note the profileSection route parameter. If the current URL is:
https://www.myawesomeapp.com/profile/my-age, the Profile component will receive a profileSection prop from the router with a (string) value of 'my-age'. This would be used to load the form "step" that asks the user her age.
Profile is a stateful component. Its render method returns the wrapped components for each step. It looks roughly this like this:
return (
<div>
<Step1 {...this.state} {...this.props} />
<Step2 {...this.state} {...this.props} />
<Step3 {...this.state} {...this.props} />
</div>
)
}
The profileSection router prop gets passed into each of the "step" components. These step components are created from an HOC. The HOC decorates each component with the following behavior:
Match a regular expression against the profileSection prop.
If it matches, the component's render method returns the markup for said step.
if it does not match, the component's render method returns null.