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
Related
The React documentation says to pass the function defined in the Root component as a prop to the Child Component if you plan to update context from a nested component.
I have implemented the same:
import React from 'react';
const DataContext = React.createContext();
/**
* The App.
*/
export default class App extends React.Component {
constructor() {
super();
this.updateGreet = this.updateGreet.bind( this );
this.state = {
greet: '',
updateGreet: this.updateGreet
}
}
updateGreet() {
this.setState({
greet: 'Hello, User',
});
}
render() {
return (
<DataContext.Provider value={ this.state }>
<GreetButton />
<DisplayBox />
</DataContext.Provider>
)
}
}
/**
* Just a button element. On clicking it sets the state of `greet` variable.
*/
const GreetButton = () => {
return (
<DataContext.Consumer>
{
( { updateGreet } ) => {
return <button onClick={ updateGreet }>Greet</button>
}
}
</DataContext.Consumer>
)
}
/**
* Prints the value of `greet` variable between <h1> tags.
*/
const DisplayBox = () => {
return (
<DataContext.Consumer>
{
( { greet } ) => {
return <h1>{ greet }</h1>
}
}
</DataContext.Consumer>
)
}
It's a very simple React App I created for learning the Context API. What I'm trying to achieve is to define the updateGreet() method within the GreetButton component instead of defining it inside the App component since the function has nothing to do with the App component.
Another advantage I see is that if I choose to remove the GreetButton component altogether, then I need not keep track of all the methods it uses defined within another components.
Is there a way we can achieve this?
I would argue that the updateGreet method does have to do with App since it is manipulating App state.
I don't see this as a context-specific issue so much as the normal react practice of passing functions down to child components.
To accomplish your wish you could bind and pass the App's setState method to the provider and then implement updateGreet in the GreetButton component, but that would be an anti-pattern and I wouldn't recommend it.
When I am working with the Context API I typically define my context in a separate file and implement a custom provider to suit my needs, passing the related methods and properties down and consuming them throughout the tree as needed.
Essentially, implement what you have in App as its own Provider class GreetProvider. In the render method for GreetProvider simply pass the children through:
render() {
return (
<DataContext.Provider value={ this.state }>
{ this.props.children }
</DataContext.Provider>
)
}
Now, all of your greeting logic can live together at the source, with the context. Use your new GreetProvider class in App and any of its children will be able to consume its methods.
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.
So I am following video tutorials by Max on Udemy and in one of the lectures he is trying to explain Ref Api's in react 16.3
So here is what he did, Inside on of the container class (not App.js) he created a property known as this.lastref = React.createRef(); and then created a ref tag in return JSX code which looks like this ref={this.lastref} (This is the parent component)
Now in child component he created a method which looks like this
myFocus () {
this.lastref.current.focus()
}
and then in parent component, he again did something like this in componentDidMount lifecycle
componentDidMount() {
this.lastref.current.myFocus()
}
Now here are two questions which I have.
[Question Part]
First: How can he use this.lastref in child component? Is this because of the uni-directional (or one directional) flow from Parent to child (this.lastPersonRef is referred from ref={this.lastPersonRef} ?
Second: myFocus I believe happens to be static method so shouldn't he initiate it before using it?
[Code Example]
Here is what Parent Component should look like -> [person.js]
import React, { Component } from 'react';
import Person from './persons/person-s';
class Cpersons extends Component {
this.lastref = React.createRef()
componentDidMount() {
this.lastref.current.myFocus()
}
render (
return {
<Person
key={el.id}
click={this.props.cpdelete.bind(index)}
ref={this.lastref}
name={el.name}
age={el.age}
changed={(event) => this.props.cpchanged(event, el.id)} />
});
}
}
export default Cpersons
and this should be my child component -> [person-s.js]
import React, { Component } from 'react';
class Cppersons extends Component {
myFocus () {
this.lastref.current.focus()
}
render() {
//something
return (
<div> Something </div>
)
}
}
export default Cppersons;
ref has changed a lot in the React world and documentation regarding it is wildy different. I suggest you use the callback method.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.otherComponentRef = null; // Will be set after the first render
}
render() {
return [
<OtherComponent ref={el => this.otherComponentRef = el} />,
<ChildComponent reference={this.otherComponentRef} />
];
}
}
First: How can he use this.lastref in child component? Is this because
of the uni-directional (or one directional) flow from Parent to child
(this.lastPersonRef is referred from ref={this.lastPersonRef} ?
when a ref is created inside a class component like this,
this.myRef = React.CreateRef();
this.myRef is assigned a null value. Later when the component is mounted, React assigns this.myRef an object with the current property making this.myRef.current an object containing either:
the dom element that the ref is attached to, or
the component that the ref is attached
In your code, lastref is attached to the Person component like so,
<Person ref={this.lastref} .../>
which at
componentDidMount() {
this.lastref.current.myFocus()
}
React assigns the Person instance (component) to this.lastref.current, like this
// ~ with a bit of React magic under the hood
this.lastref.current = new Person();
Since myFocus is a method on the instance, it can be called by this.lastref.current.myFocus()
I encourage you to read more about React Ref and its expected behavior from React docs. If you find yourself stuck, you can read about how class inheritance work in Javascript which gives more insight to what is going on behind the scenes.
Second: myFocus I believe happens to be static method so shouldn't he
initiate it before using it?
it's really just the syntax being used from a different Javascript specification
class P {
constructor(props) {
super();
this.myRef = props.myRef
}
myFocus() {
console.log(this.myRef)
}
}
is equivalent to
class P {
myFocus() {
console.log(this.props.myRef)
}
}
in the eyes of the babel-loader from Babel which transpiles the Javascript in a typical React application created with create-react-app. myFocus will be a method of the Instance when it is instantiated in both cases.
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
Say I have a comp that is inside of a Scene (react-native-router-flux). It lets people choose their favorite fruits.
import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {MKCheckbox} from 'react-native-material-kit';
var styles = StyleSheet.create({});
export default class PickAFruit extends Component {
render() {
console.log(this.props.fruits);
return (
<View>
{
this.props.fruits.map((x)=> {
return (
<View key={x.key}>
<Text>{x.key}</Text>
<MKCheckbox checked={this.props.checked} key={x.key} onCheckedChange={(e) => {
this.props.update(e, '' + x.key)
}}/>
</View>
)
})
}
</View>
)
}
}
In the parent comp I'm loading the list of fruits from an API in the didMount:
componentDidMount() {
ApiInst.getFruits().then((fruits) => {
console.log(fruits);
console.log(this.props.fruits);
this.props.fruits = fruits;
});
}
I'm also setting a default fruits array in the parent class. It seems like the properties won't load via the API though, the list of fruit is always the "unknown" value, never the new values. Do I need to load the list of fruits before the Profile scene is loaded? When is the correct time to set properties for a component if they will come from an API?
setState seems like the easy answer but these settings don't "feel" like state, they feel like properties that would be injected at build-time (i.e. when the component is built, not the app). Is this a distinction without a real difference?
You can't modify props. Props are passed from parent to child component, and only the parent can change them.
Use setState instead:
this.setState({fruits: fruits});
And access them from state:
<PickAFruit fruits={this.state.fruits} />
You may also want to set a default state in the component constructor:
constructor(props) {
super(this);
this.state = {fruits: null};
}
this.props.fruits = fruits;
won't effect child component, and to be honest - I'm not sure it will work at all. If you don't want to use flux architecture I think the best solution is to update parent's state on componentDidMount() and pass it as props to child component:
componentDidMount() {
ApiInst.getFruits().then((fruits) => {
this.setState({fruits: fruits});
});
}
render() {
return (
<PickAFruit fruits={this.state.fruits} />
);
}
Every state change will invokre render() method, so after API call PickAFruit component will be rerendered, with fruits passed as a props.