Scroll position in template React - javascript

I have a problem with scroll in React. How can I use scrolling to element in Template? I'd like to scroll to one of the elements in this.props.children by using navigation of Header.
class Template extends React.Component {
handleClick = () => {
const tesNode = ReactDOM.findDOMNode(this.refs.test);
window.scrollTo(0, tesNode.offsetTop);
};
render() {
return (
<div>
<Header click={this.handleClick} ></Header>
{this.props.children}
<Footer></Footer>
</div>
)
}
}

I've made some example here https://codesandbox.io/s/48k35w1107, check this out.

Related

Nuka-carousel react move to certain slide

I've got a question about npm package 'nuka-carousel. How to perform goToSlide on clicked element. I have list of elements with scroll3d setting. If I click on e.g last visible element I would like to scroll carousel so that element would be in a center.
According to their GitHub documentation, you can take control of the carousel just by adding onClick to your control button and then use setState() to change the slideIndex:
import React from 'react';
import Carousel from 'nuka-carousel';
export default class extends React.Component {
state = {
slideIndex: 0
};
render() {
return (
<Carousel
slideIndex={this.state.slideIndex}
afterSlide={slideIndex => this.setState({ slideIndex })}
>
...
</Carousel>
<button onClick={(event) => this.handlesClick(event, index)}> />
);
}
handleClick = (event, index) => {
event.preventDefault();
this.setState({slideIndex: index});
}
}

How to use MDCRipple.attachTo on multiple buttons in React Component

I have a simple React component that renders multiple buttons from an array in my props. I'm applying the ripple on DidMount, however, it's only attaching on the first button, the rest are being ignored. It looks like the attachTo only takes the first element. Is there another way to attach to all the buttons on didmount?
class NavBar extends Component {
constructor(props) {
super(props);
this.state = {
links
};
}
componentDidMount() {
MDCRipple.attachTo(document.querySelector('.mdc-button'));
}
render() {
return (
<section>
{this.state.links.map((link, i) => {
return (
<StyledLink key={i} to={link.url}>
<StyledButton className="mdc-button">
<StyledIcon className="material-icons">{link.icon}</StyledIcon>
<StyledTypography className="mdc-typography--caption">
{link.title}
</StyledTypography>
</StyledButton>
</StyledLink>
);
})}
</section>
);
}
}
Final markup
<a class="sc-iwsKbI bhaIR">
<button class="mdc-button sc-dnqmqq ksXmjj mdc-ripple-upgraded" style="--mdc-ripple-fg-size:57.599999999999994px; --mdc-ripple-fg-scale:2.1766951530355496; --mdc-ripple-fg-translate-start:-7.799999999999997px, 19.200000000000003px; --mdc-ripple-fg-translate-end:3.200000000000003px, 19.200000000000003px;">
...content
</button>
</a>
<a class="sc-iwsKbI bhaIR">
<button class="mdc-button sc-dnqmqq ksXmjj">
...content
</button>
</a>
Updated
I was able to find a way to use the attachTo with each button, but it still seems like there's a better way.
I changed by componentDidMount() to:
componentDidMount() {
this.state.links.forEach((link) => {
MDCRipple.attachTo(document.getElementById(`button-navbar-${link.id}`));
});
}
and then changed my render to
<StyledButton id={`button-navbar-${link.id}`} className="mdc-button">
Is there a way to do this without having to iterate through the array?
The react way to do this is to write component that injects the necessary logic.
class RippleButton extends Component {
const handleRef = elem => MDCRipple.attachTo(elem);
render() {
return (
<StyledButton {...this.props} ref={this.handleRef} />
);
}
}
Then render that component instead of your original StyledButton component and it will call the MDCRipple.attachTo() itself with its ref.
Depending on how the StyledButton is implemented you may need to use another prop to get the ref to the underlying DOM element. You did not provide enough of your code to exactly know this.

In react, how to get noticed when children change?

I am making this class called Scrollable which enables scrolling if the width/height of the children elements exceeds a certain value. Here is the code.
import React, { Component } from 'react';
const INITIAL = 'initial';
class Scrollable extends Component {
render() {
let outter_styles = {
overflowX: (this.props.x? 'auto': INITIAL),
overflowY: (this.props.y? 'auto': INITIAL),
maxWidth: this.props.width || INITIAL,
maxHeight: this.props.height || INITIAL,
};
return (
<div ref={el => this.outterEl = el} style={outter_styles}>
<div ref={el => this.innerEl = el}>
{this.props.children}
</div>
</div>
);
}
};
export default Scrollable;
// To use: <Scrollable y><OtherComponent /></Scrollable>
This works great. Except now I wish to add one more functionality which makes the scrollable always scroll to the bottom. I have some idea of how to do it:
this.outterEl.scrollTop = this.innerEl.offsetHeight;
But this only need to be called when this.props.children height changes. Is there any idea on how to achieve this?
Thanks in advance.
I would recommend a package element-resize-detector. It is not React-specific but you can easily build a high-order component around it or integrate your Scrollable component with it.
Now I have an idea of solving this.
Since I am using react-redux. The problem is that I could not use lifecycle hooks on this Scrollable component since this.props.children might not necessarily be changed when the content is updated.
One way to achieve this is to make Scroll component aware of the corresponding values in the redux state. So that when that relevant value is updated, we can scroll down to the bottom.
Scrollable component:
import React, { Component } from 'react';
const INITIAL = 'initial';
class Scrollable extends Component {
componentWillUpdate(){
if(this.props.autoScroll){
// only auto scroll when the scroll is already at bottom.
this.autoScroll = this.outterEl.scrollHeight - this.outterEl.scrollTop - Number.parseInt(this.props.height) < 1;
}
}
componentDidUpdate(){
if(this.autoScroll) this.outterEl.scrollTop = this.outterEl.scrollHeight;
}
render() {
let styles = {
overflowX: (this.props.x? 'auto': INITIAL),
overflowY: (this.props.y? 'auto': INITIAL),
maxWidth: this.props.width || INITIAL,
maxHeight: this.props.height || INITIAL,
};
return (
<div ref={el => this.outterEl = el} style={styles}>
<div ref={el => this.innerEl = el}>
{this.props.children}
</div>
</div>
);
}
};
export default Scrollable;
Scrollable container:
import { connect } from 'react-redux';
import Scrollable from '../components/Scrollable';
const mapStateToProps = (state, ownProps) => Object.assign({
state: state[ownProps.autoScroll] || false
}, ownProps);
export default connect(mapStateToProps)(Scrollable)
With this, Scrollable's life cycle hooks will be called when the corresponding state changes.

How to get a child component to control the parent in react?

I'm building a Modal component and want to make sure that it would easily fit a variety of different possible modal designs.
In my case it would have optional header, content and footer. I don't have an issue with this so far, the code looks like:
class ModalHeader extends React.Component {
render () {
const children = React.Children.map(this.props.children, _.identity);
return (
<div className="modal-header">
{children}
</div>
);
}
}
class ModalFooter extends React.Component {
render () {
const children = React.Children.map(this.props.children, _.identity);
return (
<div className="modal-footer">
{children}
</div>
);
}
}
class ModalContent extends React.Component {
render () {
const children = React.Children.map(this.props.children, _.identity);
return (
<div className="modal-content">
{children}
</div>
);
}
}
class Modal extends React.Component {
render () {
var header, footer, content = null;
React.Children.map(this.props.children, function(child){
if (child.type === ModalHeader) {
header = child;
}
if (child.type === ModalFooter) {
footer = child;
}
if (child.type === ModalContent) {
content = child;
}
});
return (
<div>
{header}
{content}
{footer}
</div>
);
}
}
Now comes the issue of closing the modal. I want it to be possible to close the modal from clicking an element that would be in any of the sub components, no matter, to the left or the right or anything and potentially nested deeply in the markup specific to that component.
Having a component that would wrap a piece of markup be it an X, a close icon or a button, making sure that when that element is clicked, the whole modal is closed.
class ModalCloser extends React.Component {
render () {
const children = React.Children.map(this.props.children, _.identity);
return (
<div onClick={this.close} className="modal-closer">
{children}
</div>
);
}
close () {
// no idea what goes here!!
}
}
There doesn't seem to be in React any easy way for a child component to communicate with the parent.
I would be fine with passing a prop to that closer element that would be the callback function to close the main modal, but at the place where I would be defining the modal, I don't see any way that it would be available:
<Modal>
<ModalHeader>
<div>
<header>
<h1>Title!!</h1>
<div class="float-right">
<ModalCloser closeHandler={???}>
X
</ModalCloser>
</div>
</header>
</div>
</ModalHeader>
<ModalContent>
...
</ModalContent>
</Modal>
Alternatively, I see that I could recursively traverse all the descendants and maybe do something to all descendants of type ModalCloser but I also don't really see a way to do that available.
What would be a good solution allowing to pass such a sub-component as a child or descendant to keep the layout flexibility while giving it the possibility to close the modal in such a case?
Pass a callback from the parent to the child and on close modal, call the callback in the child.
Here is a simpler version of your code, where modalClose is the callback that's being passed to the parent from the child. You can also test it in jsfiddle.
class ModalCloser extends React.Component {
render () {
return (
<div onClick={this.props.close}>
{this.props.children}
</div>
);
}
}
class Main extends React.Component {
modalClose() {
alert('closing')
}
render() {
return (<ModalCloser close={this.modalClose}>X</ModalCloser>);
}
}
ReactDOM.render(
<Main />,
document.getElementById('container')
);
In your jsfiddle you have defined the callback in the render method, therefore it was executed on every rerender of the component. I would strongly recommend you to read Thinking in React multiple times - it will answer all your questions.
Pseudocode
<Parent>
modalClose () {
console.log('modal closed...')
}
<Child modalClose={modalClose} />
<Parent/>
Now in your < Child /> whenever modal closes:
closeHandler = { this.props.modalClose }

Toggle Class in React

I'm using react for a project where I have a menu button.
<a ref="btn" href="#" className="btn-menu show-on-small"><i></i></a>
And a Sidenav component like:
<Sidenav ref="menu" />
And I wrote the following code to toggle the menu:
class Header extends React.Component {
constructor(props){
super(props);
this.toggleSidenav = this.toggleSidenav.bind(this);
}
render() {
return (
<div className="header">
<i className="border hide-on-small-and-down"></i>
<div className="container">
<a ref="btn" href="#" className="btn-menu show-on-small"><i></i></a>
<Menu className="menu hide-on-small-and-down"/>
<Sidenav />
</div>
</div>
)
}
toggleSidenav() {
this.refs.btn.classList.toggle('btn-menu-open');
}
componentDidMount() {
this.refs.btn.addEventListener('click', this.toggleSidenav);
}
componentWillUnmount() {
this.refs.btn.removeEventListener('click', this.toggleSidenav);
}
}
The thing is that this.refs.sidenav is not a DOM element and I cant add a class on him.
Can someone explain me how to toggle class on the Sidenav component like I do on my button?
You have to use the component's State to update component parameters such as Class Name if you want React to render your DOM correctly and efficiently.
UPDATE: I updated the example to toggle the Sidemenu on a button click. This is not necessary, but you can see how it would work. You might need to use "this.state" vs. "this.props" as I have shown. I'm used to working with Redux components.
constructor(props){
super(props);
}
getInitialState(){
return {"showHideSidenav":"hidden"};
}
render() {
return (
<div className="header">
<i className="border hide-on-small-and-down"></i>
<div className="container">
<a ref="btn" onClick={this.toggleSidenav.bind(this)} href="#" className="btn-menu show-on-small"><i></i></a>
<Menu className="menu hide-on-small-and-down"/>
<Sidenav className={this.props.showHideSidenav}/>
</div>
</div>
)
}
toggleSidenav() {
var css = (this.props.showHideSidenav === "hidden") ? "show" : "hidden";
this.setState({"showHideSidenav":css});
}
Now, when you toggle the state, the component will update and change the class name of the sidenav component. You can use CSS to show/hide the sidenav using the class names.
.hidden {
display:none;
}
.show{
display:block;
}
refs is not a DOM element. In order to find a DOM element, you need to use findDOMNode menthod first.
Do, this
var node = ReactDOM.findDOMNode(this.refs.btn);
node.classList.toggle('btn-menu-open');
alternatively, you can use like this (almost actual code)
this.state.styleCondition = false;
<a ref="btn" href="#" className={styleCondition ? "btn-menu show-on-small" : ""}><i></i></a>
you can then change styleCondition based on your state change conditions.
Toggle function in react
At first you should create constructor
like this
constructor(props) {
super(props);
this.state = {
close: true,
};
}
Then create a function like this
yourFunction = () => {
this.setState({
close: !this.state.close,
});
};
then use this like
render() {
const {close} = this.state;
return (
<Fragment>
<div onClick={() => this.yourFunction()}></div>
<div className={close ? "isYourDefaultClass" : "isYourOnChangeClass"}></div>
</Fragment>
)
}
}
Please give better solutions
Ori Drori's comment is correct, you aren't doing this the "React Way". In React, you should ideally not be changing classes and event handlers using the DOM. Do it in the render() method of your React components; in this case that would be the sideNav and your Header. A rough example of how this would be done in your code is as follows.
HEADER
class Header extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<div className="header">
<i className="border hide-on-small-and-down"></i>
<div className="container">
<a ref="btn" href="#" className="btn-menu show-on-small"
onClick=this.showNav><i></i></a>
<Menu className="menu hide-on-small-and-down"/>
<Sidenav ref="sideNav"/>
</div>
</div>
)
}
showNav() {
this.refs.sideNav.show();
}
}
SIDENAV
class SideNav extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
render() {
if (this.state.open) {
return (
<div className = "sideNav">
This is a sidenav
</div>
)
} else {
return null;
}
}
show() {
this.setState({
open: true
})
}
}
You can see here that we are not toggling classes but using the state of the components to render the SideNav. This way, or similar is the whole premise of using react. If you are using bootstrap, there is a library which integrates bootstrap elements with the react way of doing things, allowing you to use the same elements but set state on them instead of directly manipulating the DOM. It can be found here - https://react-bootstrap.github.io/
Hope this helps, and enjoy using React!
For anybody reading this in 2019, after React 16.8 was released, take a look at the React Hooks. It really simplifies handling states in components. The docs are very well written with an example of exactly what you need.

Categories

Resources