I have a dumb component, List, that has some methods defined like this:
class List extends React.Component {
...
scrollTo() {
...
}
clear() {
...
}
}
I then use it in a Parent Component, let's say UsersList:
class UsersList extends React.Component {
render() {
return <List {...this.props} {...} />;
}
}
Then I have as a Parent I have FriendsPage:
class FriendsPage extends React.Component {
render() {
return (
...
<UsersList ref={(ref) => { this.usersListRef = ref; }} {...} />
);
}
}
I'd like to be able to call this.usersListRef.scrollTo() for example in FriendsPage, without having to define the methods of List in UsersList.
I can pass a prop called listRef and use it as ref={this.props.listRef} but I was wondering if another solution exists.
You can't call functions of a child and that would also be against the idea of react. Ideally your <UserList> component accepts a prop that makes it know where to scroll to. Something like:
class UserList extends React.Component {
componentDidUpdate() {
const {activeItem} = this.props;
this.scrollTo(activeItem);
}
scrollTo = activeItem => {
// calculate position of active item to scroll to
// and scroll to it
}
}
And then your <FriendsPage> could look something like this:
class FriendsPage extends React.Component {
handleSelectionChange = selected => {
// triggered when the selected element in the list changes
this.setState({selected});
}
render() {
const {selected} = this.state;
return <UserList activeItem={selected} {...this.props} />;
}
}
It's hard to tell if this is 100% the approach you need as you did not provide many details about the conditions that lead to scrolling.
Mmmm, I'm not sure if I'm getting It right, but you should read this: https://reactjs.org/docs/thinking-in-react.html
In React, the idea is to go top-down. As you need the UsersList component to do something when user interacts with List component, then you should define the function in UsersList and pass that function as a prop to the List Component.
For example:
class List extends React.Component {
<div onClick={this.props.scrollTo}
}
I then use it in a Parent Component, let's say UsersList:
class UsersList extends React.Component {
scrollTo(){
do something...
}
render() {
return <List scrollTo={() => this.scrollTo()} {...this.props} {...} />;
}
}
Then I have as a Parent I have FriendsPage:
class FriendsPage extends React.Component {
render() {
return (
...
<UsersList {...} />
);
}
}
I forgot to check the documentation on this one, and there is a paragraph about it here: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components.
Basically, it is the solution I envisaged in my question, using a listRef and passing it down to wherever my List Component is.
Thanks, everyone!
Related
I have a class extending a custom component which extends another custom component. I want to override handleReload() function to add some extra functionality. I don't want to completely change it's behavior. The problem is that I get errors even when the page loads, and it lacks any functionality.
To sum it up a little bit, there are 3 layers of components, parent -> child -> grandchild, handleReload() is declared in child.
export default class Child extends Parent {
handleReload() {
return () => {
const { name, load } = this.props
load(name)
}
}
<ReloadButton
value={'action.reload'}
onClick={::this.handleReload()}
privilege={`${metaForm(name)}:Read`}
/>
}
Now what I am trying to do is something like this:
export class GrandChild extends Child {
handleReload() {
super.handleReload()
// something else here
}
}
You would need to evaluate something in handleReload to tell it whether it's coming from a grandChild or not and then use a if statement of some sort to do this or that.
// ----- parent.js
export class ParentContainer extends Component {
import {GrandChild} from './components/grandchild';
handleReload = type => {
if(type === 'grandchild'){
// something
} else {
// something else
}
}
render() {
return (
<GrandChild onReload={this.handleReload} />
)
}
}
Example of passing it back in props
// / ----- /components/grandchild.js
export class GrandChild extends Component {
render() {
return (
<button onClick={this.props.onReload('grandchild')}
);
}
}
I am having a child component a parent component. I am having a function in child component which returns some jsx what i want to do is use that function to return the same jsx in parent component but iam unable to figure out a way to do that. I am giving my minimal code:
parent component:
class App extends Component {
render() {
return (
<div className="App">
<Player ref={instance=>{this.player = instance}} />
{this.player.func('aaa.com','bbb')}
</div>
);
}
}
export default App;
Child component:
import React, { Component } from "react";
class Player extends Component {
func = (url, label) => {
return (
<button onClick={() => this.func(url)}>
{label}
</button>
)
}
render() {
return <div>1</div>;
}
}
export default Player;
Error: Cannot read property 'func' of undefined
//
Note: i know i can use the jsx in parent component by copy-pasting but iam trying to figure out a way of doing like this. I am having doubt that is it even possible
You can create a Player object and access the function using that object.
new Player().func('aaa.com','bbb')
I don't quite understand what you need exactly but I think that you're looking to pass some jsx element from the Child component to the parent component. What we can do is declare a propType callback on the child component and then implement it on the parent component like so.
import React from 'react';
class Hello extends React.Component {
constructor() {
super();
this.state = {
// this state will keep the element returned by the parent
returnElements: null
}
this.onReturn = this.onReturn.bind(this);
}
// this method will be fired when the Child component returns callback for onSomethingReturned
onReturn(element) {
this.setState({
returnElements: element
})
}
render () {
return (
<div>
<h1>Hello, React!</h1>
<Child onSomethingReturned={this.onReturn} />
{/* I am going to display the state here */}
{this.state.returnElements}
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const element = <h3>this is child element</h3>;
// will call the propType callback function with a element I want to return
this.props.onSomethingReturned(element);
}
render() {
return (null);
}
}
export default Hello;
Let's say this.props.children[0] is TouchableOpacity which is from parent.
And I want add extra callback to it in HERE. Is what I want possible? I can't find any solution for this.
Thanks.
export default class NewClassXXX extends React.Component{
render()
{
newchildren = React.Children.map(this.props.children, (child) => {
//child is TouchableOpacity class in my code ***
//I want to add LongPress callback by coding HERE. (like below)
child.onLongPress = () => console.log('SUCESSS!!!'); //this code does not work.
return child;
}
return {newchildren}
}
Use React.cloneElement method to achieve this like this
export default class NewClassXXX extends React.Component{
render()
{
newchildren = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {onLongPress: () => console.log('SUCESSS!!!')})
}
return {newchildren}
}
I encapsulated some HTML code to a extra class and want to hand over a function to it now.
The parent class looks like this:
class Home extends React.Component {
doSomething = id => {
console.log(id);
// here are some fetch operations only available in Home component
};
render() {
return (
<Child doSomething={() => this.doSomething} />
)
}
}
my Child component then looks like this:
const id = 3;
const Child = ({doSomething}) =>
(
<Button onClick={doSomething(id)}>Click</Button>
);
export default Child
I was trying around with different solutions but either I get no result or the onClick function is called when the Home component is rendered instead of when clicking on the button.
I want the function to be executed when the button is clicked. And the id parameter should be handed over as well. I can't have the function in the Child component itself since I have to use some redux actions in it which are not available in the child class.
I know this is not a too difficult question but I'm still a noob with JavaScript..
Edit: I accomplished having the event parameter inside my function but I wonder how to access the id with it. I can't simply add a prop to the Button element since it does not allow that.
Thanks in advance for your help,
Erik
You need to bind method in constructor and pass it to child component
class Home extends React.Component {
constructor() {
this.doSomething = this.doSomething.bind(this);
}
doSomething(id) {
console.log(id);
// here are some fetch operations only available in Home component
}
render() {
return <Child doSomething={this.doSomething} />
}
}
And in Child
const Child = ({doSomething}) =>
(
<Button onClick={() => doSomething(id)}>Click</Button>
)
I think you need something like this:
this.doSomething.bind(this);
It binds this as the first argument of your function, which is needed when you pass a class method as a reference. When doSomething is called in the child component, this will reference the parent component.
First of all your jsx is wrong. Your are missing
render() {
return ...;
}
There is no valid JSX Button
<Button onClick={doSomething(id)}>Click</Button>,
use <button> tag instead.
Here is working example.
const element = <h1>Hello, world</h1>;
class Home extends React.Component {
doSomething = id => {
console.log(id);
// here are some fetch operations only available in Home component
};
render() {
return <Child doSomething={() => this.doSomething('do something input')} />;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
//console.log(props);
}
render() {
return <button onClick={this.props.doSomething.bind(this)}>Click</button>;
}
}
ReactDOM.render(
<Home />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js"></script>
<div id="root"></div>
You probably need to pass the id when you are defining the fat arrow function
class Home extends React.Component {
doSomething(id) {
console.log(id);
}
render() {
return <Child doSomething={(id) => this.doSomething(id)} />
}
}
Firstly, in Home component use arrow function as doSomething prop to preserve correct context (alternatively you can use bind in component constructor):
class Home extends React.Component {
doSomething(id) {
console.log(id);
// here are some fetch operations only available in Home component
}
render() {
<Child doSomething={() => this.doSomething()} />
}
}
and then use arrow function that will call passed function with given value as click handler in child component:
<Button onClick={() => doSomething(id)}>Click</Button>
From what I understand, HOCs in ReactJS add props to your decorated component, but I want to add methods that can also act on the state.
As an example, I generally never call this.setState() without checking this.isMounted() first. In essence, I want:
export default ComposedComponent => class BaseComponent extends React.Component {
static displayName = "BaseComponent";
constructor(props) {
super(props);
}
//------> I want this method to be available to any ComposedComponent
//------> And it has to act upon the state of ComposedComponent
updateState(obj) {
if (this.isMounted() && obj) {
this.setState(obj);
}
}
render() {
return (
<ComposedComponent {...this.props} {...this.state} />
)
}
}
Say I want to decorate my component Home. So I'd just return it as export default BaseComponent(Home).
But this.updateState() is not available inside Home class. How do I solve this?
Okay, I figured it out. I had spent too much time on this, so I hope this answer could help somebody out as well. Short answer: add the method in your decorator to props, then bind it in your decorated class' constructor.
Here is the code:
export default ComposedComponent => class BaseComponent extends React.Component {
static displayName = "BaseComponent";
constructor(props) {
super(props);
// Note how I am adding this to state
// This will be passed as a prop to your composed component
this.state = {
updateState: this.updateState
}
}
updateState(obj) {
this.setState(obj);
}
render() {
return (
<ComposedComponent {...this.props} {...this.state} />
)
}
}
And here is an example of a class that would use it (I'm using ES7 for simplicity):
#BaseComponent
class Home extends React.Component {
static displayeName = 'Home';
constructor(props) {
super(props);
// And here I am binding to it
this.updateState = this.props.updateState.bind(this);
}
render() {
return (
<div>Hi</div>
)
}
}