Exposing function of a React component - reactjs

Is there a way to expose a function of a React component?
class MyComponent extends Component {
someFunction() {
// I'd like to expose this function
}
render() {
return(
<div>Hello World</div>
);
}
}
In the above example, I'd like to expose someFunction() so that I can call it from the parent component.
In other words, I'm trying to call a function of a sub-component from the parent component.
Two questions:
Can I do this? If so, how?
This may be a more important question: Should I be doing this? I'm always mindful that something I may need now may be exposing an architectural flaw. So, I want to know if I should even be calling a sub-component's function from the parent component.

While it is possible to call a function on a child component, I don't think it would be idiomatic.
If you want to change the way a component renders, you should alter its props and trigger a re-render.
Keeping the interface for a component exclusively through the props helps to better encapsulate the inner workings of the component.
For your specific example, I would rather have a visible toggle that you alter and let the datePicker component decide for itself what it wants to do with that property value.

Can I do this?
Yes, We can achieve that by using ref, assign ref to MyComponent when rendering inside parent component then use that ref to call the function.
How?
Check this snippet:
class Parent extends React.Component {
_callChildComponent(){
this.child.someFunction();
}
render() {
return(
<div>
<div onClick={() => this._callChildComponent()}>Call</div>
<MyComponent ref={el => this.child = el}/>
</div>
);
}
}
class MyComponent extends React.Component {
someFunction() {
console.log('hello');
}
render() {
return(
<div>Hello World</div>
);
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>
But i will suggest you to pass a function from parent component to child component and use that function to pass data or for any other purpose instead of using ref.

Related

How to change a property value of this.state in the child component's child

Can't figure out how to set a specific property within this.state in parent component, from a child component of it's child component. I know how to pass a function to a child component and have it effect the parent that passed it, but it seems tricky to pass a function to a child of a child and have that set values in this.state of it's parent's parent. So this is three levels deep rather than two.
I don't necessarily need code or examples, although they will be welcomed. I just need the right angle to approach this from. Do I just pass a function from top level parent (through props) to the child, and the child never calls it and instead merely pipes it through via props to it's child where it is actually called in a click event? Not sure how this can connect back to parent of parent and change this.state values
Yes, passing functions down the chain of children is the expected way to manage this in vanilla react.
If you find your state management needs are becoming complex enough that this no longer suffices, consider looking into a state management framework like Redux.
You can use the Context API. You can also use store such as Redux. Store data in the redux store, not the component, and use the actions in child to change data
const Context = React.createContext();
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
color: 'white'
};
}
render() {
return (
<Context.Provider value={this.setState.bind(this)}>
<React.Fragment>
<div style={{background: this.state.color}}>123</div>
<Child1/>
</React.Fragment>
</Context.Provider>
);
}
}
class Child1 extends React.Component {
render() {
return (
<span>
child1
<Child2/>
</span>
);
}
}
class Child2 extends React.Component {
render() {
return (
<Context.Consumer>
{(setState) => (
<button onClick={() => { setState({color: 'red'}); }}>child2</button>
)}
</Context.Consumer>
);
}
}
ReactDOM.render(<Parent/>, document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Using React, is it possible to define a component inside the definition of another component?

(this question differs to 'is it possible to use a component inside another', this question is 'can I define a component inside the definition of another', it is not a duplicate of 'Can I write Component inside Component in React?')
Is it possible to define a component inside the definition of another component? This way I can use the props of the outside component in the inner component. It would keep the code more concise. Something like the following...
class AComponent extends Component {
CustomButton = (props) => {
let { disabled, ...otherProps } = props // <-- props of the inner component
const {isDisabled, currentClassName} = this.props // <-- props of the main component
return (
<button
className={className}
disabled={isDisabled}
{...otherProps}>
</button>
)
}
render() {
return (
<div>
<CustomButton>Add something</CustomButton>
<CustomButton>Delete something</CustomButton>
<CustomButton>Edit</CustomButton>
</div>
)
}
}
If the custom button was defined on its own (the usual way of defining components) I would have to do something like below which is ok but more verbose and less dry as I repeat the definition of {...buttonProps} for each component
let buttonProps = {
className: this.props.currentClassName,
disabled: this.props.disabled
}
return (
<div>
<button {...buttonProps}>Add something</button>
<button {...buttonProps}>Delete something</button>
<button {...buttonProps}>Edit</button>
</div>
)
While yes, it's possible to define one function component inside another function component, this is not recommended.
Referring to the ReactJs docs:
reactjs.org/docs/components-and-props
reactjs.org/docs/conditional-rendering
Most, if not all, examples show child components being defined outside of the parent component.
Defining a component inside another will cause the child component to be re-created on mount and unmount of the parent, which could cause unexpected behavior if the child is using props from the parent, and cannot handle if those props are suddenly undefined.
It's best to define components separately.
Yes! I needed to define the function component in the render method...which makes sense
class AComponent extends Component {
render() {
const ButtonLocal = (props) => {
const {isDisabled, currentClassName} = this.props
return (
<Button
disabled={isDisabled}
className={currentClassName}
{...buttonProps}>
</Button>
)
}
return (
<div>
<ButtonLocal>Add something</ButtonLocal>
<ButtonLocal>Delete something</ButtonLocal>
<ButtonLocal>Edit</ButtonLocal>
</div>
)
}
}
Very much possible to add components inside a parent component. Consider following example:
<ParentComponent style={styles}>
<ChildComponent style={styles} {...props} />
</ParentComponent>

React rerenders whole component when its properties change

Let's consider the following sample:
import React, {Component} from 'react';
class B extends Component {
render() {
console.log(`Render runs with ${this.props.paramA}`);
return (<div>{this.props.paramA}</div> );
}
}
class App extends Component {
constructor() {
super();
this.state = {paramA: 'asd'};
}
handleChange(event) {
this.setState({paramA: event.target.value});
}
render() {
return (<div>
<input value={this.state.paramA} onChange={e => this.handleChange(e)}/>
<label>
<B paramA={this.state.paramA}></B>
</label>
</div>);
}
}
Here's the gif of how it works.
If you noted, in order to update the changes from properties, react needs to evaluate "render" method. That causes the whole component to update instead of its small part that really changed (check the gif, the div element blinks in chrome developer tools):
TL;DR According to react philosophy,apps should be written in a way to have as many dummy components as possible. That means we have to pass properties a few level down sometimes (other time we can use e.g. redux), which leads to a lot of render methods that evaluate every time the property of top level component changes. With all that being said I often see in the real life react application that a whole root div blinks when e.g. users types something into input. Well even if it's a browser "lag" I don't really like the idea that react reevaluates all components (meaning running their render method) when a component needs to update only its small part.
The question:
Am I doing something wrong? Is there a way to implement react component so they update only things that changed?
It sounds like you're looking for the shouldComponentUpdate lifecycle hook.
Pretty self explanatory; if the component should only re-render under specific prop/state changes, you can specify those in this hook, and return false otherwise.
In this case, React is not rerendering the entire component but the first parent of the dynamic part of them. In this case, the <div> is the parent (and the entire component so you're right), but in this fiddle wrapping {this.props.paramA} inside a paragraph tag, the <div> is not the direct parent, so just rerenders <p> tag and <div> does not need to update.
class B extends React.Component {
render() {
console.log(`Render runs with ${this.props.paramA}`);
return (<div><p>{this.props.paramA}</p></div> );
}
}
class App extends React.Component {
constructor() {
super();
this.state = {paramA: 'asd'};
}
handleChange(event) {
this.setState({paramA: event.target.value});
}
render() {
return (<div>
<input value={this.state.paramA} onChange={e => this.handleChange.bind(this)(e)}/>
<label>
<B paramA={this.state.paramA} />
</label>
</div>);
}
};
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>

What is the different between the ref={callback} and the ref = "myInput" in react?

as the link https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute
It then only gives an example of using the component immediately. I'm trying to find out how i would use this function to access the component immediately, and save the component for future use, as it says we are able to do.
The difference is using ref={callback} react passes the responsibility of managing the reference storage back to you. When you use ref="sometext", under the covers react has to create a refs property on your class and then add all the ref="sometext" statements to it.
While its nice to have a simple this.refs.sometext access to components its difficult and error prone on the react side to clean up this refs property when the component is destroyed. It's much easier for react to pass you the component and let you handle storing it or not.
According to the react docs
React will call the ref callback with the DOM element when the
component mounts, and call it with null when it unmounts.
This is actually a pretty slick idea, by passing null on unmount and calling your callback again you automatically clean up references.
To actually use it all you have to do is access it from any function like so:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in this.textInput.
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
The callback you set on ref will receive the component as the first parameter, the 'this' word will be the current class 'CustomTextInput' in this example. Setting this.textInput in your callback will make textInput available to all other functions like focus()
Concrete Example
Tweet from Dan Abermov showing a case where ref callbacks work better
Update
Per Facebook Docs using strings for refs is consider legacy and they "recommend using either the callback pattern or the createRef API instead."
When you assign a ref={callback} like <input type="text" ref={(input) => {this.textInput = input}}/> what basically you are doing is saving the ref with the name textInput for future use. So instead of using ref="myInput" and then using this.refs.myInput we can use the call back bethod and then access the component later like this.textInput.
Here is a demo for the same, whereby we are accessing the input value using ref on button click
class App extends React.Component {
constructor(){
super();
}
handleClick = () => {
console.log(this.textInput.value);
}
render() {
return (
<div>
<input type="text" ref={(input) => {this.textInput = input}}/>
<button type="button" onClick={this.handleClick}>Click</button>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.4/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.4/react-dom.min.js"></script>
<div id="app"></div>
With React 16.3, you can use React.createRef() API instead:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}

reactjs - Is it possible to pass a property to a component as following?

wonder if it is possible to pass a component a property as following
ReactDOM.render(
<ContainerBox anotherComponent={<AnotherComponent />} />, document.body);
And then insider the ContainerBox I want to pass AnotherComponent a property in following way.
class ContainerBox extends React.Component {
clickHandler() {
//does something fun
}
render () {
return (
this.props.anotherComponent(this.clickHandler) //<----- is it possible to pass properties from here?
);
}
}
Is it possible to pass things from ContainerBox to AnotherComponent from that position?
ContainerBox has a clickHandler function which I want to pass to AnotherComponent. It is possible to do so if I move <AnotherComponent /> to inside of render() instead. But then I cannot reuse ContainerBox for other components without first copying the whole ContainerBox.
Does it make sense? Hope you can understand.
UPDATED code example
Yes, that is possible. However, it's more common to do it like this
ReactDOM.render(
<ContainerBox><AnotherComponent /></ContainerBox>, document.body);
And in ContainerBox
class ContainerBox extends React.Component {
render () {
return (
this.props.children
);
}
}
Read more about reacts this.props.children here: https://facebook.github.io/react/docs/multiple-components.html#children
Edit:
I just want to point out that in this example, we are not passing a component, but an element (the result of rendering the component).
It's also possible to pass components, like this:
<Foo buttonComponent={FancyButtonComponent} />
and in Foo:
render() {
Button = this.props.buttonComponent;
return (
<div>
...
<Button />
</div>
);
}

Resources