How to get scroll properties in react-simplebar (in stateful function) - reactjs

I am new with refs in react.js and in the react-simplebar documentation it just shows how to get the scroll ref for a stateless function.
class App extends React.Component {
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar ref={this.refs.scroll}><h1>scrollable element</h1></Simplebar>
)
}
}

For anyone coming to this at a later date. This is how I managed to set the scrolling position after a few hours of digging around in a function component
const SimpleScrollerComponent = () => {
// Create a reference for the SimpleBar component so we can acces it
const scrollableNodeRef = React.createRef();
const handleScrollDownBtnClicked = () => {
// This is where we set the scroll position
scrollableNodeRef.current.scrollTop = 1200;
};
return (
<div>
{/* We attach the reference to the component */}
<SimpleBar scrollableNodeProps={{ ref: scrollableNodeRef }}>
{/* This is where your code goes inside the scroll box */}
</SimpleBar>
<Button onClick={handleScrollDownBtnClicked}>Scroll to the Bottom</Button>
</div>
);
};

try this
class App extends React.Component {
constructor(props) {
super(props);
this.scrollableNodeRef = React.createRef();
}
onChangeScrollToTop() {
this.scrollableNodeRef.current.scrollTop = 0;
}
render() {
console.log(this.refs.scroll) // => Undefined
return (
<Simplebar scrollableNodeProps={{ ref:this.scrollableNodeRef }}>
<h1>scrollableelement</h1>
</Simplebar>
)
}
}

Related

How can I correctly pass state as props from one component to another?

I'm trying to pass my state as props from component Locatione.js to Map.js, so the props are available when I call the function SendLocation in Map.js.
Here is my component Locatione
export default class Locatione extends Component {
state = {
location: null
};
componentDidMount() {
this._getLocationAsync();
}
_getLocationAsync = async () => {
let location = await Location.getCurrentPositionAsync({ });
this.setState({ location });
console.log("log this pls", this.state); // the state here logs correctly
};
render() {
return (
<Map locatione={this.state} /> // when accesing this props in Map, I'm getting **null**
);
}
}
Here is my Map.js component
export default class Map extends React.Component {
sendLocation() {
console.log("sending location log", this.props); // the props here appear as null
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, () => console.log("hi", this.props))} //the props here log correctly
/>
);
}
}
I also tried passing my props in this fashion, to no avail.
export default class Map extends React.Component {
sendLocation(altitude, longitude) {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, (this.props)))}
/>
);
}
}
Thanks for your help
There is a little problem here:
onPress={(this.sendLocation, () => console.log("hi", this.props))}
The console.log will trigger everytime the code renders or re-renders the button, not when you click it.
If you want to log after you call a function change the onPress to:
onPress={() => {
this.sendLocation()
console.log("hi", this.props)
}}
The other problem is that you are not giving your sendLocation function access to this.
You have two ways of doing it:
First way: Binding it inside your constructor. So inside your Map.js you add it like:
constructor(props){
super(props);
this.sendLocation.bind(this);
}
Second way: Declaring your sendLocation function as an arrow function:
sendLocation = () => {
console.log("sending location log", this.props);
}
Just as you can pass regular values as props, you can also grab data from a component’s state and pass it down as props for any of its child components. You just need to pass the exact value, also use constructor in case of class components.
`export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
location: null
};
}
render() {
return (
<Map location={this.state.location} />
);
}
}`
You need to pass the function to onPress and use arrow function to be able to use this inside sendLocation.
class Map extends React.Component {
sendLocation = () => {
console.log('sending location log', this.props.locatione); // the props here appear as null
};
render() {
return (
<Button
title="Send Sonar"
onPress={this.sendLocation}
/>
);
}
}
You are passing the props through components correctly, but you should use arrow function and also anonymous func.
Try:
export default class Map extends React.Component {
sendLocation = (altitude, longitude) => {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={()=>this.sendLocation}
/>
);
}
}

ReactJS Assigning Value inside a class component method to props

I'm trying to access the index of the selected tab in a React component so as to map it to props as follows:
class AttendanceDetail extends React.Component {
handleSelect(key, props) {
console.log(key)
props.index = key;
}
render(){
const {single_class, courses, attendances} = this.props;
// console.log(this.state);
if(single_class) {
return(
<div className='container content-section'>
// Some irrelevant Code
<Tabs defaultActiveKey={0} onSelect={this.handleSelect} id="uncontrolled-tab-example">
{ courses.map((course, index) => {
return (
<Tab eventKey={index} title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>
)
})}
</Tabs>
</div>
)
} else {
return (
<div className='container content-section'>
Loading Unit details...
</div>
);
}
}
}
So basically the handleSelect method is what determines the index of the selected tab and logs it to the console. The problem is, I'm tring to map that key (index) to props so as to access it else where but to no avail. Could someone help me out? What am I missing?
You're not supposed to set the component's props, only read. You can use state within the component:
export class Wrapper extends React.Component {
constructor() {
this.state = {
index: 0 //initial state
}
}
handleSelect(index, props) {
this.setState({index})
}
render() {
return (
<span>{this.state.index}</span>
)
}
}
you can read more on the official docs.
if i understood the scenario correctly, you need to log index value of the currently active tab. try using onFocus event handler to get the index value of the currently visible tab and set the state that will be used by handleSelect
constructor(props){
super(props);
this.state = {
index:''
}
}
the handler definition
setIndex = (index) => {
this.setState({index})
}
update handleSelect
handleSelect(index) {
console.log(index)
// call event handler of parent component eg: this.props.getIndex(index);
}
update tabs component handler
<Tabs defaultActiveKey={0} onSelect={() => {this.handleSelect(this.state.index)}} id="uncontrolled-tab-example">
call handler on focus of tab
<Tab
onFocus={() => {this.setIndex(index)}}
eventKey={index}
title={course.course + " year " + course.yearofstudy}>
//Other irrelevant code...
</Tab>

React Native binding functions over .map()

So I am having some trouble combining concepts of .map() and function binding. I am using .map() in the same way ngFor is used in angular, to place a custom button component on the page for every item in a user's account.
Here is some example code:
class MyButton extends Component {
constructor(props) {
super();
this.state = {
progress: 0
}
}
render() {
return(
<TouchableWithoutFeedback onPress={this.pressFunction}>
(...more code inside)
</TouchableWithoutFeedback>
)
}
pressFunction = () => {
(animate progress from 0 to 1 for some animation)
}
}
/////////////////////////////////////////////////////////////////
class Parent extends Component {
render() {
return(
{
this.props.data.array.map(obj => {
return(
<View style={someStyle}>
<MyButton data={obj} />
</View>
)
})
}
)
}
}
So in the Parent Component, multiple MyButtons are rendered properly, each according to the passed object from the array. However, when any button is pressed, all of the pressFunctions for all MyButtons fire.
My question is I guess, how do I ensure that each pressFunction of each MyButton is bound only to the specific instance of the MyButton? I am having trouble with the scope here.
My understanding is that
functionName = () => {}
should properly bind the function to the instance, but I have tried the older ways as well with the same result.
I solved this by creating a dynamic ref on each object mapped to a MyButton, using a unique property of each obj in the array:
this.props.data.array.map(obj => {
return(
<View style={someStyle}>
<MyButton ref={obj.name} data={obj} />
</View>
)
})
Still don't know why my it didn't bind uniquely without a ref
You should pass onPress as a props. Below is the updated code
class MyButton extends Component {
constructor(props) {
super();
this.state = {
progress: 0
}
}
render() {
return(
<TouchableWithoutFeedback onPress={this.props.onPress}>
(...more code inside)
</TouchableWithoutFeedback>
)
}
}
/////////////////////////////////////////////////////////////////
class Parent extends Component {
pressFunction = () => {
(animate progress from 0 to 1 for some animation)
}
render() {
return this.props.data.array.map(obj => {
return(
<View style={someStyle}>
<MyButton
data={obj}
onPress={this.pressFunction}
/>
</View>
)
})
}
}

Children ref undefined react native

I created a component wrapper around ViewPagerAndroid (simplified version)
class TabView extends Component {
constructor(props){
super(props)
this.state = { position: 0 }
}
changePage = (key) => {
this._pagerRef.setPage(key)
this.setState({position: key})
}
render(){
return(
<ViewPagerAndroid ref={(ref) => this._pagerRef = ref}>
{ this.props.scenes }
</ViewPagerAndroid>
)
}
}
I want to trigger changePage from outside the component (eg from: <TabView ref={(ref) => this._ref = ref} />, and run this._ref.changePage(key)).
However, each time I try to do so, this._pagerRef is undefined inside the changePage function of TabView.
What am I missing ?
There is a more idiomatic React solution to the problem you are trying to solve -- namely making TabView a controlled component and setting ViewPager page on componentDidUpdate:
class TabView extends Component {
componentDidUpdate = ({ page }) => {
// call setPage if page has changed
if (page !== this.props.page && this._pagerRef) {
this._pagerRef.setPage(page);
}
};
render() {
return (
<ViewPagerAndroid
initialPage={this.props.page}
ref={ref => this._pagerRef = ref}
onPageSelected={e => this.props.pageChanged(e.nativeEvent.position)}
>
{this.props.scenes}
</ViewPagerAndroid>
);
}
}
You can then move the current page tracking to the parent component's state and pass it down to TabView as a prop, along with a handler that updates it when the value changes:
render() {
return (
<TabView
page={this.state.page}
pageChanged={page => this.setState({page})}
/>
)
}
You're trying to access the ref from outside of the component which has no instance to it.
Therefore you need to pass it as a prop from the parent component itself. Also you need to move the changePage to the parent component to access it from outside.
Parent
changePage = (key) => { //... Call the function here
this._pagerRef.setPage(key)
this.setState({position: key})
}
accessRef (ref) {
this._pagerRef = ref . //... Bind the ref here
}
<TabView passRef={this.accessRef} /> //...Pass the ref here
Child
<ViewPagerAndroid ref={this.props.passRef}> . // ... Set the ref here
{ this.props.scenes }
</ViewPagerAndroid>

How do I call the openDrawer function on DrawerLayoutAndroid from parent component?

I have made a component which sets up the DrawerLayoutAndroid, which I want to call in my index file.
drawer.js:
export default class MenuDrawer extends Component {
constructor(props) {
super(props);
this.openDrawer = this.openDrawer.bind(this);
}
render() {
var navigationView = (
// ...
);
return (
<DrawerLayoutAndroid
ref={(_drawer) => this.drawer = _drawer}
drawerWidth={200}
drawerPosition={DrawerLayoutAndroid.positions.Left}
renderNavigationView={() => navigationView}>
{this.props.children}
</DrawerLayoutAndroid>
);
}
openDrawer() {
this.drawer.openDrawer();
}
}
I then have everything in the render() function in my index file wrapped around since I want the drawer to be accessible from anywhere. I just cannot figure out how I open the drawer from the index file. I have tried several different ways to call the function, but I always end up getting undefined is not an object.
index.android.js
export default class AwesomeProject extends Component {
constructor(props) {
super(props);
this.openMenu = this.openMenu.bind(this);
}
render() {
const { region } = this.props;
return (
<MenuDrawer
ref={(_menudrawer) => this.menudrawer = _menudrawer}
style={styles.layout}>
<TouchableHighlight
style={styles.topbar}
onPress={this.openMenu}>
<Text>Open</Text>
</TouchableHighlight>
</MenuDrawer>
);
}
openMenu() {
this.refs.menudrawer.openDrawer();
}
}
This gives me the error "undefined is not an object (evaluating 'this.refs.menudrawer.openDrawer')".
How do I go about solving this?
Thanks
It looks good, you're just accessing the menudrawer incorrectly. It should be:
this.menudrawer.openDrawer();
Because your ref is:
ref={(_menudrawer) => this.menudrawer = _menudrawer}

Resources