Currently I have a class component that contains functions that act as components in my JSX.
Example:
class MyComponent extends React.Component {
MySubComponent = (props) => {
if (props.display) {
return <p>This text is displayed</p>
}
}
render() {
return (
<this.MySubComponent display={true} />
)
}
}
Are there any repercussions to calling components this way? Also is there a term for this?
This results in creating new MySubComponent function for each MyComponent instance, which is not very efficient way of doing this.
There may be only two benefits of having MySubComponent as MyComponent method.
One of them is that MySubComponent can be replaced with another implementation in MyComponent subclass without modifying render function. This isn't idiomatic to React because it promotes functional approach instead of OOP.
Another is that MySubComponent can access MyComponent instance and its properties. This results in design problem because two components are tightly coupled.
Neither of these arguments is substantial in React development. Unless there are specific needs that require MySubComponent to be a part of MyComponent, the former shouldn't be defined as instance method of the latter. It could be just:
const MySubComponent = (props) => {
if (props.display) {
return <p>This text is displayed</p>
}
}
class MyComponent extends React.Component {
render() {
return (
<MySubComponent display={true} />
)
}
}
Related
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
In my app I have several form field components that all behave the same way but look slightly different. I want to be able to use the state and class methods of a single formfield component while providing some sort of alternative render method so that I can customize the appearance of this element on the fly. I know I can wrap children and then use the props.children in the component. But I'm looking to re-use the components methods somehow:
class Parent extends React.Component {
render() {
<div className="parent">
<ChildFormField
renderAlternate={self => {
return (
<div className="child--alternate">
<input onChange={self.doThing} />
</div>
);
}}
/>
</div>
}
}
// And the child component look something like...
class ChildFormField extends React.Component {
state = {
value: null
}
doThing = value => {
return this.setState({ value });
}
render() {
if (this.props.renderAlternate !== undefined) {
return this.props.renderAlternate();
}
// standard return
return <div />;
}
}
I'm relatively new to React outside of its basic usage. Is there a recommended way to achieve this functionality?
Your renderAlternate function expects a parameter self. So you need to pass this when calling it.
return this.props.renderAlternate(this);
See https://codesandbox.io/s/w759j6pl6k as an example of your code.
This recipe is known as render prop. It's widely used where it's suitable, but here it looks like bad design decision, primarily because it exists to access component self instance. This is the case for inheritance:
class AlternateChildFormField extends ChildFormField {
render() {
return (
<div className="child--alternate">
<input onChange={this.doThing} />
</div>
);
}
}
In React, function composition is usually preferred, but inheritance is acceptable solution if it serves a good purpose. ChildFormField requires doThing to be a method and not helper function because it needs to access this.setState.
An alternative is to use React 16.7 hooks and functional components. This way the same component can be expressed with composition:
const useThingState = () => {
const [state, setState] = useState({ value: null });
return value => {
return setState({ value });
};
}
const ChildFormField = props => {
// not used here
// const doThing = useThingState();
return <div />;
}
const AlternateChildFormField = props => {
const doThing = useThingState();
return (
<div className="child--alternate">
<input onChange={doThing} />
</div>
);
}
The common way this is managed in React ecosystem are High Order Components:
ref: https://reactjs.org/docs/higher-order-components.html
ref: https://medium.com/backticks-tildes/reusing-react-component-logic-with-higher-order-component-3fbe284beec9
Further, what you are looking for maybe a reuse of state logic.
As React 16.7-alpha you can use hooks to reuse state logic with functional components but APIs are subject of possible breaking changes.
ref: https://medium.com/#nicolaslopezj/reusing-logic-with-react-hooks-8e691f7352fa
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.
With ES6, it is possible to represent a React Component as a function.
So, the following component
class MyComponent extends React.Component {
render() {
<div>Hi</div>
}
}
could also be represented as such
const MyComponent = (props) => (
<div>Hi</div>
)
My question is whether the functional representation also allows for static properties. So, I'm wondering whether it is possible to represent the following component somehow as a function as well:
class MyComponentWithStaticProperty extends React.Component {
static myProperty = {'hello': 'world'}
render() {
<div>Hi</div>
}
}
const MyComponent = (props) => (
<div>Hi</div>
)
MyComponent.myProperty = {'hello': 'world'};
I'm currently inheriting an ES6 React base component in the following way:
model.js (base component):
class ModelComponent extends React.Component {
render() {
// Re-used rendering function (in our case, using react-three's ReactTHREE.Mesh)
...
}
}
ModelComponent.propTypes = {
// Re-used propTypes
...
};
export default ModelComponent;
Then I have two extending components that both look basically like this:
import ModelComponent from './model';
class RobotRetroComponent extends ModelComponent {
constructor(props) {
super(props);
this.displayName = 'Retro Robot';
// Load model here and set geometry & material
...
}
}
export default RobotRetroComponent;
(Full source code here)
This appears to work fine. Both models are showing up and working as I would expect.
However, I have read in multiple places that inheritance is not the correct approach with React - instead I should be using composition. But then again, Mixins are not supported in React v0.13?
So, is the approach I'm taking above OK? If not, what's the problem, and how should I do this instead?
The Facebook team recommends 'using idiomatic JavaScript concepts' when writing React code, and since there is no mixin support for ES6 classes, one should just use composition (since you are just making use of idiomatic Javascript functions).
In this case, you can have a composeModal function that takes a component and returns it wrapped in a higher-order, container component. This higher-order component will contain whatever logic, state, and props you want passed down to all of its children.
export default function composeModal(Component){
class Modal extends React.Component {
constructor(props){
super(props)
this.state = {/* inital state */}
}
render() {
// here you can pass down whatever you want 'inherited' by the child
return <Component {...this.props} {..this.state}/>
}
}
Modal.propTypes = {
// Re-used propTypes
...
};
return Modal
}
Then you can use the composition function like so:
import composeModal from './composeModal';
class RobotRetroComponent extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Retro Robot';
// Load model here and set geometry & material
...
}
render(){
return /* Your JSX here */
}
}
export default composeModal(RobotRetroComponent);