How can a React component control the scroll position of a sibling component?
Parent is the parent component, it has as children List (which contains a scrollable div) and Actions with a button that should control scrolling of the List scrollable div.
Some options:
Maintain a reference to the DOM element of the scrollable div and scroll position in the Redux Store. Trigger scrolling in the reducer on state changes.
Have Parent manage scroll. Somehow Parent needs to have a DOM reference to the scrollable div in List, not sure how List can pass up a ref.
Use something like react virtualized (VirtualScroll) to show virtual content in List. Don't actually scroll, just update the content to what would be seen at the new scroll position. This means we can't animate the scroll?
Option #2 seems most reasonable (animated scroll is important for this context), but I'm not sufficiently familiar with best practices in React/Redux to make good architecture decisions.
I would go for option 2. But the parent should not have to keep a reference to the List items other than having them as child components. You can save the the scroll position in the redux store, have the parent control it, and then pass it down as a prop to your List items.
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import List from './List';
class Parent extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
scrollPos: PropTypes.number.isRequired
};
updateScrollPos() {
const value = getValueFromSomewhere();
this.dispatch({ type: UPDATE_SCROLLPOS, value })
}
render() {
return <div>
<button onClick={::this.updateScrollPos}>Update scrollPos</button>
<List scrollPos={this.props.scrollPos} />
<List scrollPos={this.props.scrollPos} />
<List scrollPos={this.props.scrollPos} />
</div>
}
}
const select = (state) => ({
scrollPos: state.scrollPos
})
export default connect(select)(Parent);
Related
I'm currently learning React, and trying to get a sense of how components re-render. I have this parent component which renders three items. Each item just renders an <li>
function App() {
console.log("Parent Rerendered");
return (
<div>
<ul>
<Item1 />
<Item2 />
<Item3 />
</ul>
</div>
);
}
Item2 is a bit different because it also renders an "x" that will un-render the component when it's clicked:
function Item2() {
const [visible, setVisible] = useState(true);
const makeInvisible = () => {
setVisible(false);
};
console.log("Item 2 Rerendered");
return visible ? (
<div>
<li>
Second Item <span onClick={makeInvisible}>X</span>
</li>
</div>
) : null;
}
When I test this in my browser and click the "x", I can see from the console that Item2 gets re-rendered. However, none of the other components get re-rendered including the parent component. However the parent component does change, so how does this happen without re-rendering it.
If that's a bit confusing, here's an illustration of the initial state, my expectations, and reality. What am I misunderstanding about how React re-renders components?
A component rerenders when it sets state, or when its parent rerenders1. App has no state and no parent, so it will never rerender. It doesn't need to though. React saves the virtual DOM from the previous render, so it still knows that App is supposed to be a div surrounding a ul surrounding an Item1, Item2, and Item3. If the Item2 rerenders, and returns a null instead of a div, react will update the part of the real DOM that the Item2 is responsible for, by removing the div. The rest of the page remains intact
1) or if a context it consumes changes, or in a class component when you call forceUpdate. But for most cases, it's just state and parent that matter.
Instead of clicking onto <Item2 /> go to the Dev-tools -> Explorer -> select the Element and press delete. The view will also change, the gap will close, without react being involved at all.
React is responsible to update the DOM, the layout is done by the browser. So when <Item2 /> decides it wants to be rendered as null instead of a div>li (??? invalid markup ) and therefore removes the respective DOM-nodes, the browser will update the layout.
And the parent component has nothing to do with all that.
I have a section that is made out of 2 components:
trigger component
show/hide component
Basically when I click on trigger component, new component under will appear. This is handled simple by useState
const [toShow, setToShow] = useState(0);
And then I am using onClick method:
onClick={() => setToShow(1)}
Showing the element under trigger component:
{toShow === 1 && (
<div className="show-box" id="myHiddenComponent">
<HiddenComponent />
</div>
)}
Problem is, I need to access the hidden component with id="myHiddenComponent" from my Navbar. That means, the component will be not set to show. But link should understand that it has to set it to be opened and then scroll to the component. Is something like that possible with React ?
Right now I am using Hashlink
<HashLink smooth to='#myHiddenComponent'>
GO TO HIDDEN SECTON
</HashLink>
But that does not work unfortunately, since the section is hidden.
you need to set state of toShow to 1 onClick on HashLink component
i want to connect a react components, that doesn't have a parent and child connection, but i want to show and hide from the child of another components.
here it is the code :
import React, { useEffect } from "react";
import DashSidebar from "./DashSidebar";
import Catalog from "./components/catalog";
function Dashboard() {
useEffect(() => {
document.title = "Home";
}, []);
return (
<div id="l-dashboard">
<DashSidebar />
<Catalog />
</div>
);
}
export default Dashboard;
i have a button in DashSidebar and i want to show and hide Catalog
why i don't make it parent and child ?
because DashSidebar is a fixed position, and it is a side panel to the left, while i want to show and hide the content in the right side..
since you are using react-router this can be done with the below steps.
Wrap your Catalog list in the side bar with the Link component .
Now render the Catalog on the right with the help of Route .
Please refer the sandbox
React SideBar Example
Lift the state of the Catalog's visibility to the Dashboard component by passing a function in DashSidebar's props that changes a variable in Dashboard's state. Pass the variable in Catalog's props to control its visibility.
I have created a React application. In this application, I've created following components:
App Component (root) : where data is loaded into state
CardList Component: List of Cards, data is passed to it using props
Card Component: use forEach to pass data to Card and it has button
CustomButton Component: acts like a button with style
What I want is when the user clicks button on any of the Card, a number should get increased everytime. But I am not able to access Data here.
Can anyone help?
Here is the Card Component for your reference:
class Card extends Component {
render() {
return (
<div className="tc bg-light-green dib br3 pa3 ma2 grow bw2 shadow-5">
<h2>Votes: {this.props.votes}</h2>
<div>
<img src={this.props.pic} alt='Superstar'/>
<div>
<h2>{this.props.name}</h2>
<p>{this.props.email}</p>
</div>
</div>
<ActionButton btnText="Vote Now!" clickhandler={this.onVoteButtonClick}/>
</div>
);
}
onVoteButtonClick = (event) => {
console.log('It was clicked : '+this.props.id);
}
}
Two options which you should research more to see what suits your need best:
Redux (or something similar)
The new Context API available in React 16.
The gist of either solution is that you're managing application state independently of the dependent component tree(s). The Context API is arguably easier to implement whereas you'll currently find many more examples explaining the Redux approach as it's still the most common solution right now.
So I am using 'react-popout' from https://github.com/JakeGinnivan/react-popout to create a popout window has this subWindow component which has its own data fields in it. I have a MainComponent which defines a list of these popoutwindows (there can be mulitple) and as I define this list I also define that the PopoutWindow has a child subWindow (I leave PopoutWindow component package alone). The PopoutWindow is a wrapper.
So I call the subWindow as part of PopoutWindow inside MainComponent and I want to be able to change the data that I pass into subWindow (currentWindowData) and trigger a re-render but right now it is not working AND I want to only change the data of just one of these PopoutWindows in the list, and not any others.
How can I have it so that upon say a button click, I change the data of only ONE of the subWindows but not all of them (assuming multiple PopoutWindows are rendered and in the list)?
<MainComponent>
openWindow() {
const keyId = shortid.generate();
const window= <PopoutWindow
title={keyId}
options={{height: '700px', width: '350px'}}
key={keyId}
>
{
<subWindow key={shortid.generate()}
data={this.state.currentWindowData}
/>}
</PopoutWindow>;
this.setState({
renderSubWindow: true,
subWindowArray: [window, ...this.state.subeWindowArray],
});
}