How do I access a React Class method from outside? - javascript

Lets say I have a component defined like this -
// actioncomponent.js
import React from 'react';
class ActionComponent extends React.Component {
state = {
isAction: false;
}
doAction = () => {
this.setState({isAction: true})
}
render () {
return (
<div>
Some render stuff..
</div>
)
}
}
export default ActionComponent
From another completely different file I want to set the state for the first component without rendering it in the new file so I need not use refs or props.
// newfile.js
import ActionComponent from './actioncomponent.js'
ActionComponent.doAction()
I'm aware the doAction can't be exported and calling it static doesn't have access to state either. How do I achieve something like this?

In React ecosystem you probably don't need this.
You can pass this method to a child component:
class ActionComponent extends React.Component {
state = {
isAction: false
}
doAction = () => {
this.setState({isAction: true})
}
render () {
return (
<div>
<Child doAction={this.doAction} />
</div>
)
}
}
And then in a Child component you can fire this action
// ...
render() {
<button onClick={() => props.doAction()}>Test</button>
}
If you need to fire action on parent, instead of child you might want to structure your state on upper level, or lift state up.
You can also achieve similar goal without drilling props, but you'll need some state management tool, e.g. Redux or in some cases Context API would be a great fit.

Related

How to access a function in a child class component from a stateless parent?

I have a redux-store with objects of initial values. And this store will get updated at a few places within the child component.
I created a stateless functional component as parent
const Parent = () => {
const store = useSelector(state => state);
const getInitState = () => {
depends on store, it will return an object as initial state for child component
}
let initState = getInitState(); //it has to be let instead of const, it could be changed during useEffect
useEffect(() => {
some initialization on mount
}, [])
return ( // return is simplified here
<Child initState={iniState} />
)
}
export default Parent;
I have a class child component something like below
class Child extends Component {
state = {
componentState: this.props.initState
}
....
}
export default Child;
I can't modify the child component It's a very complex component with many sub components which I dont handle.
Now I need to access setState function of child component from parent. Or I need to change the state of child from parent, is there a way to do that?
Yes, I understand a new design should be consider since it's anti-pattern, but I am just wondering if I can do it under current setting.
Thank you all in advance.
==============================================================
Edit: For whoever runs into the same problem, functional component does not support constructor. So I have included a breif correction to the answer.
Define parent as below
import React, { useRef } from "react";
const Parent = () => {
const childRef = useRef(null);
return (
<Child ref={childRef} />
)
}
export default Parent;
Then you are able to use childRef.current to access all function from child component.
The best way is using a react Context , and set state in parent then the child consume state of parent (using react hooks would be so easy than class component)
but in your case as you mentiened (I wonder I can do it under current setting)
you can use react refs :
first put ref prop in your rendered component tag then use it in parent to execute function that's declared inside child
as below :
inside parent component :
const Parent = () => {
.
.
.
constructor() {
//create react ref for our component
this.childComponent = React.createRef();
}
callChildFunction() {
// here using refs you can access function in you child refenrenced component
this.childComponent.cuurent.doSomeUpdateStateStuff(newState);
}
return ( // return is simplified here
<Child ref={this.childComponen} initState={iniState} />
)
...
}
and your child :
class Child extends Component {
state = {
componentState: this.props.initState
}
doSomeUpdateStateStuff(state) {
// stuff updating state callled from parent
}
....
}

How to pass a state to multiple other classes in React

I started learning React approx. month ago and I'm very confused about the concept because its still something new to me(compared to my previous work in C++ and C).
To quickly summarize I would like to know what is React's equivalent of C++ return form a function. How would I return value(or values) from a function(in my case class functions/states) and use it in my other components.
I have made an simple script that changes background to simulate RGB light on mouse and I made it so the HSL color mode is applied to the background of the component. I would like to use this on multiple components,icons, etc on my page but it feels like there is a better way than importing all functions in three files making the work triple than requiered.
import React, { Component } from 'react'
import './colorStrip.scss'
class ColorStrip extends Component {
constructor(props) {
super(props)
this.colorHue=10;
this.colorSaturation=100;
this.colorLightness=50;
this.state = {
color:"hsl(0,100%,50%)"
}
this.changeColor(1);
}
changeColor = (speed) => {
this.colorHue+=10*speed;
if(this.colorHue>=360)
this.colorHue=0;
this.setState({
color : "hsl("+this.colorHue+","+this.colorSaturation+"%,"+this.colorLightness+"%)"
})
setTimeout(() => {this.changeColor(speed)},75)
}
render() {
return (
<svg style={{backgroundColor:this.state.color}} className="strip">
</svg>
)
}
}
export default ColorStrip
So I would like to use this.state.color(or this.state.colorHue or any state) in three other SVG components on my page.
I really looked some of the other answers but they were quite complex and requiered multiple returns which was confusing.
There are a couple different options you can use to achieve this.
One would be to move your function that calculates the colour to a higher level component (so one of the parent components), that has the child components you want to pass this state to, and then pass your state down through component props.
class parent extends component {
// your functions to calculate your colour
render () {
return <div>
<ChildComponent colourProp={this.state.color} />
<ChildComponent colourProp={this.state.color} />
<ChildComponent colourProp={this.state.color} />
</div>
}
}
Another option if you need the colour to change based on the child component, is to pass down the function that alters the colour to the child component. So similar to the example above, but you would pass down the colour changing function to the child as well.
<ChildComponent colourProp={this.state.color} changeColour={this.changeColourFunction}/>
Now you can call that function from your child
// Inside child component
this.props.changeColour(params)
And now your parent will change its state, and the new colour will get changed in the parent and passed down to all the children.
Lastly you can try using ReactContext, set it up in a file that's external to all your components and and import it to your components. In your parent component where you pass your initial state, you would use YourContext.Provider and pass your initial state. Then in your children you can use YourContext.Consumer. For more details on this see : https://reactjs.org/docs/context.html
As Jonathan said, you can pass state as props to other components, but only if they are connected. If the svgs you are using are not being rendered in the same file, things will become a little messy. In order to 'fix' this, people use state management tools, such as redux and context API.
Redux, for example, is built based on database design, so you can access the state globally. Tough it is really useful, the environment is not beginners friendly, and I do not advise you learning it until completely grasping react.
Try this way:
import './colorStrip.scss'
class ColorStrip extends Component {
constructor(props) {
super(props)
this.colorHue=10;
this.colorSaturation=100;
this.colorLightness=50;
this.state = {
color:"hsl(0,100%,50%)"
}
this.changeColor(1);
}
changeColor = (speed) => {
this.colorHue+=10*speed;
if(this.colorHue>=360)
this.colorHue=0;
this.setState({
color : "hsl("+this.colorHue+","+this.colorSaturation+"%,"+this.colorLightness+"%)"
})
setTimeout(() => {this.changeColor(speed)},75)
}
render() {
const { color } = this.props;
return (
<svg style={backgroundColor:color} className="strip">
</svg>
)
}
}
export default ColorStrip
I'd suggest creating a Higher-Order Component (HOC) to house the color logic and then you can wrap any component you want with this HOC and the wrapped component will have the logic & data you need.
For example:
import React, { Component } from "react";
function withColor(WrappedComponent) {
return class ComponentWithColor extends Component {
constructor(props) {
super(props);
this.colorHue=10;
this.colorSaturation=100;
this.colorLightness=50;
this.state = {
color:"hsl(0,100%,50%)"
}
this.changeColor(1);
}
changeColor = (speed) => {
this.colorHue+=10*speed;
if(this.colorHue>=360)
this.colorHue=0;
this.setState({
color : "hsl("+this.colorHue+","+this.colorSaturation+"%,"+this.colorLightness+"%)"
})
setTimeout(() => {this.changeColor(speed)},75)
}
render() {
const { color } = this.state;
return <WrappedComponent color={ color } { ...this.props }/>
}
}
}
Then if you define a new component, and you want it to have access to the color prop, just wrap the component class/function in withColor before constructing.
For example:
class MyComponent extends Component {
render() {
const { color } = this.props;
return (
<svg style={backgroundColor:color} className="strip">
</svg>
)
}
}
const MyComponentWithColor = withColor(MyComponent);
// then export & use MyComponentWithColor

React pass function to dynamic created child with additional parameter

I want to dynamically create child components, receiving an onClick event from their parent/grandparent component in React. During the creation I want to add a parameter to the onClick-event. Basically the desired flow is:
When rendering parent component
Pass the reference to the desired function to the creation of the dynamic component
In process of creating the dynamic component I want to add a parameter, defined by the creator
the onClick event in the child should call the onClick function in the parent using the parameter it got from the creator of the dynamic component
For the code: this is the dynamic component creator and the parent
import React from 'react';
// This is the creator of my dynamic components
// It currently sets this.props.name as parameter for the parent function
class CreateComponent extends React.Component {
render(){
return(
<div className="childBox">
// this.props.component is a react component of type ImageBox (see next code block)
{React.cloneElement(this.props.component, {
open: this.props.open(this.props.name),
close: this.props.close,
})}
</div>
)
}
}
// This is the parent component, using the creator and some state to open/close different components
export class DynamicContentGrid extends React.Component {
constructor() {
super();
this.state = { activeComponent: '' };
}
close() {
this.setState({ activeComponent: '' });
}
open(component) {
this.setState({ activeComponent: component })
}
render() {
console.log(this.props.children);
return(
<div className={css(styles.grid)}>
<div className={css(styles.boxUpperLeft, styles.box)}>
<CreateComponent
component={this.props.children['upperLeft']}
name='upperLeft'
open={() => (name) => this.open(name)}
close={() => this.close()}
/>
</div>
</div>
)
}
}
export default DynamicContentGrid;
And here comes the very basic child component using this.props.close without parameters (they should be set in the creator):
import React from 'react';
export class ImageBox extends React.Component {
render() {
const {title, link, img} = this.props.content.front;
return(
<div>
<h1>{title}</h1>
<h2 onClick={this.props.open}>{link}</h2>
<img src={img} />
</div>
)
}
}
export default ImageBox;
What works
The dynamic rendering of child components works fine.
Where it breaks
As you can see, the magic happens in open={() => (name) => this.open(name)}. What I want is: pass this.open to the creator, set open(name) as parameter and pass on the open function to the child.
Everything works fine, if I said the "name" parameter directly in the parent, but for several reasons I do not want to do this. So I need some kind of currying but I can't figure out, what is wrong. The parameter "name" is not properly set in the creator at the moment.
In CreateComponent set open: () => this.props.open(this.props.name).
Also, remove () => (name) => this.open(name) and replace with this.open and put this.open = this.open.bind(this); into the constructor.

Initialize state with dynamic key based on props in reactJS

How to initialize state with dynamic key based on props? The props is a data fetched from external source (async). So the props will change when the data is succesfully downloaded. Consider a component like this.
edit: I want to make the state dynamic because I want to generate a dialog (pop up) based on the item that is clicked. the DialogContainer is basically that. visible prop will make that dialog visible, while onHide prop will hide that dialog. I use react-md library.
class SomeComponent extends React.Component {
constructor() {
super();
this.state = {};
// the key and value will be dynamically generated, with a loop on the props
// something like:
for (const item of this.props.data) {
this.state[`dialog-visible-${this.props.item.id}`] = false}
}
}
show(id) {
this.setState({ [`dialog-visible-${id}`]: true });
}
hide(id) {
this.setState({ [`dialog-visible-${id}`]: false });
}
render() {
return (
<div>
{this.props.data.map((item) => {
return (
<div>
<div key={item.id} onClick={this.show(item.id)}>
<h2> Show Dialog on item-{item.id}</h2>
</div>
<DialogContainer
visible={this.state[`dialog-visible-${item.id}`]}
onHide={this.hide(item.id)}
>
<div>
<h1> A Dialog that will pop up </h1>
</div>
</DialogContainer>
</div>
);
})}
</div>
)
}
}
// the data is fetched by other component.
class OtherComponent extends React.Component {
componentDidMount() {
// fetchData come from redux container (mapDispatchToProps)
this.props.fetchData('https://someUrlToFetchJSONData/')
}
}
The data then is shared via Redux.
However, based on my understanding so far, state can be updated based on props with componentWillReceiveProps or the new getDerivedStateFromProps (not on the constructor as above). But, how to do that on either method?
The example here only explains when the state is initialized on the constructor, and call setState on either cWRP or gDSFP. But, I want the key value pair to be initialized dynamically.
Any help/hint will be greatly appreciated. Please do tell if my question is not clear enough.
import React from 'react';
import {connect} from 'react-redux';
import {yourAction} from '../your/action/path';
class YourClass extends React.Component {
state = {};
constructor(props){
super(props);
}
componentDidMount(){
this.props.yourAction()
}
render() {
const {data} = this.props; //your data state from redux is supplied as props.
return (
<div>
{!data ? '' : data.map(item => (
<div>{item}</div>
))}
</div>
)
}
}
function mapStateToProps(state) {
return{
data:state.data //state.data if that is how it is referred to in the redux. Make sure you apply the correct path of state within redux
}
}
export default connect(mapStateToProps, {yourAction})(YourClass)
If you do this, <div>{item}</div> will change as you change the data state. The idea is to just map the redux state to your class props - you don't have to map the props back to the state. The render() automatically listens to changes in props supplied by redux. However, if you do want to somehow know redux state change in events, you can add the following functions.
componentWillReceiveProps(newProps){
console.log(newProps)
}
getDerivedStateFromProps(nextProps, prevState){
console.log(nextProps);
console.log(prevState);
}

React Executing a method inside a component

I have a component like this:
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props, context) {
super(props, context);
this.state = {
isActive: false,
}
}
showMyComponent() {
this.setState({
isActive: true,
});
}
hideMyComponent() {
this.setState({
isActive: false,
});
}
render() {
return (
<div>
<h1>Compoent Here</h1>
</div>
);
}
}
export default MyComponent;
Now, on my index.js I am adding several components.
...
<Header />
<Nave />
Can I now do something like this here:
MyComponent.showMyComponent();
Like you normally call a function?
If not, how is this done?
You can use references. In your render() method you can get the ref. e.g.
<MyComponent ref={ref => {this.myComponent = ref}}/>
You need to create a field myComponent and assign it to it. With that you can call it like this.myComponent.showMyComponent()
See here Refs and the DOM
Use State
You are thinking about react wrong. You should not have to call a components function like this ever.
You can pass a prop to the component that will make the component hide or show.
or wrap the component in a if in the parent. Use the parents state to hide or show the component.
Like
if (someCondition) {
<MyComponent />
}
It's doable, even if some people hates this option, cause it's not the official React way, true.
You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as this.refs.myTypeahead.reset()). In most cases, it's clearer to use the built-in React data flow instead of using refs imperatively.
But However, thinking out of the box, is not forbidden so you can use refs for this.
class Parent extends Component {
onSomeThing() {
// Call some method of myChild
this.myChild.myChildsPublicMethod()
}
render() {
return <MyChild ref={ref => { this.myChild = ref; }} />
}
}
// MyChild
// Just as demo using Pure components here.
// You could use the normal class notation..
const MyChild = () => <div>Ola</div>;
MyChild.someMethod = () => console.log('Ola');
More here https://zhenyong.github.io/react/docs/more-about-refs.html

Categories