Recently I've been working in a project where I need to keep the code as cleanest as possible. That's why I'm working in a component which needs to extends some characteristics from another one, but I also need to pass some props to obtain some behavior in return. But there is a problem: I'm adding a prop in the constructor, but there is a function that doesn't receive/seem have that prop:
class SampleClassOne extends PureComponent<Props>{
Constructor(Props){
console.log(props) // In this console.log, the variable aditionalVar appears
super(props){
{
}
functionOne(){
const {
varOne,
varTwo,
varThree,
aditionalVar
} = this.props
console.log(this.props) // In this console.log, the variable aditionalVar does not appear
render(){
...
}
}
}
class SampleClassTwo extends SampleClassOne {
Constructor(Props){
super({...props, aditionalVar: true}) // This is where I'm adding the new variable
}
}
}
I don't know if I'm clear with the question I'm asking... how can I make sure that new variables I'm adding from components that extends from others can be received by all the component that extends its class.
Thanks for your help
Related
I am javascript and React newbie, so I am still a little bit confused by thinking in React concept.
I am trying to make simple object inspector in React.
Here is property row element:
class PropertyRow extends React.Component {
constructor(props) {
super(props);
this.state = {
propertyName: this.props.propertyName,
propertyValue: this.props.propertyValue
};
alert(this.props.propertyName + " evoked in constructor");
}
render() {
return (
<div>{this.props.propertyName} = {this.props.propertyValue}</div>
// <div>{this.state.propertyName} = {this.state.propertyValue}</div>
);
}
}
here in the component PropertyRows I am trying to read all properties of an object dynamically.
class PropertyRows extends React.Component {
constructor(props) {
super(props);
this.createProRows = this.createProRows.bind(this);
}
createProRows(obj) {
const propArr = [];
for (const key of Object.keys(obj)) {
const val = obj[key];
propArr.push(<PropertyRow propertyName={key} propertyValue={val} />);
}
return propArr;
}
render() {
return <div>{this.createProRows(this.props.obj)}</div>;
}
}
And here I test this marvelous code
class Express extends React.Component {
constructor(props) {
super(props);
this.state = {
soldiers: 0,
captain:'John Maverick'
};
this.doClick = this.doClick.bind(this);
}
doClick() {
const obj = {
soldiers: this.state.soldiers + 1,
country:'Australia' //add new property
};
this.setState(obj);
}
render() {
return (
<div onClick={this.doClick}>
<PropertyRows obj={this.state} />
</div>
);
}
}
ReactDOM.render(<Express />, document.getElementById("root"));
When you click on the text, you will see incrementing "soldiers" property by one. The code is buggy and I do not understand why, or perhaps I do, but I have not absolutely no idea, what how to solve it in React metalanguage.
I would expect, that dynamically created array of <PropertyRow propertyName={key} propertyValue={val}/> would be nice way to browse object properties. But it seems, that the rendered HTML DOM objects are not destroyed and recreated. They are mysteriously reattached, when the new object in the doClick function is to be expressed.
Furthermore
When create another object in doClick, the property obj.captain is still there (in the browser window), probably because the underlying HTML DOM elements are not destroyed. Adding new property country: 'Australia' seems to work OK.
When I call <PropertyRow propertyName={key} propertyValue={val}/> the second time I would expect, that constructor would be fired, because it is created and pushed in the new array. But it is not. It is fired only for the new property country: 'Australia'
It seems, that I have to somehow destroy rendered HTML DOM elements in order to force react to recreate them. But how?
Or is there another way?
I deeply apologize for this long text. I hope it's not so complicated to read.
Thanx
delete obj.captain doesn't do anything because there's no captain key in obj. captain key exists in this.state, and deleting it from it is discouraged because React state is conventionally immutable.
The use of this.state together with this.setState may potentially result in race condition, state updater function should be used instead.
It should be:
doClick() {
this.setState(state => ({
soldiers: state.soldiers + 1,
country:'Australia',
captain: undefined
}));
}
The problem with PropertyRow is that it processes props once in constructor. PropertyRow constructor is fired only once because the component has been already mounted and now it is only updated on new props (here's illustrative diagram of React lifecycle hooks).
If a state is supposed to derive from received props, getDerivedStateFromProps hook should be used, it maps a state from props before initial render and next updates. In this case a state is not needed because state properties and props don't differ. It's enough to have:
class PropertyRow extends React.Component {
render() {
return (
<div>{this.props.propertyName} = {this.props.propertyValue}</div>
);
}
}
And PropertyRow could be rewritten as functional component because it doesn't benefit from being a class.
This question already has answers here:
What is the difference between using constructor vs state = {} to declare state in react component?
(3 answers)
Closed 4 years ago.
I saw different examples where some people use state while others use this.state. I still don't understand when to use what. In examples using Mapbox or Meteor I only see this.state.
ex1:
export default class SomeComponent extends Component {
state = { someState }
render() {
return (
<Something-to-render-that-uses-state>
);
}
}
ex2:
export default class SomeComponent extends Component {
constructor(props) {
super(props);
this.state = { someState }
}
render() {
return (
<Something-to-render-that-uses-state>
);
}
}
when you define a variable in class as Object class property initialized, you don't need to make use of this keyword, simply writing state = { someState } is enough, However if you want to define a class variable in a function or a constructor, you need to make use of this keyword to specify what scope the variable belongs to
export default class SomeComponent extends Component {
constructor(props) {
super(props);
this.state = { someState }
}
render() {
return (
<Something-to-render-that-uses-state>
);
}
}
The state = {} declaration is a class property, which is currently not part of the JavaScript language. Certain utilities such as Babel will compile this into legal JavaScript code.
So it's exactly the same thing. Take a look at the JavaScript that babel transpiles the code into. There is no difference.
When is it important to pass props to super(), and why?
class MyComponent extends React.Component {
constructor(props) {
super(); // or super(props) ?
}
}
There is only one reason when one needs to pass props to super():
When you want to access this.props in constructor.
Passing:
class MyComponent extends React.Component {
constructor(props) {
super(props)
console.log(this.props)
// -> { icon: 'home', … }
}
}
Not passing:
class MyComponent extends React.Component {
constructor(props) {
super()
console.log(this.props)
// -> undefined
// Props parameter is still available
console.log(props)
// -> { icon: 'home', … }
}
render() {
// No difference outside constructor
console.log(this.props)
// -> { icon: 'home', … }
}
}
Note that passing or not passing props to super has no effect on later uses of this.props outside constructor. That is render, shouldComponentUpdate, or event handlers always have access to it.
This is explicitly said in one Sophie Alpert's answer to a similar question.
The documentation—State and Lifecycle, Adding Local State to a Class, point 2—recommends:
Class components should always call the base constructor with props.
However, no reason is provided. We can speculate it is either because of subclassing or for future compatibility.
(Thanks #MattBrowne for the link)
In this example, you are extending the React.Component class, and per the ES2015 spec, a child class constructor cannot make use of this until super() has been called; also, ES2015 class constructors have to call super() if they are subclasses.
class MyComponent extends React.Component {
constructor() {
console.log(this); // Reference Error
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
By contrast:
class MyComponent extends React.Component {
constructor() {
super();
console.log(this); // this logged to console
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
More detail as per this excellent stack overflow answer
You may see examples of components created by extending the React.Component class that do not call super() but you'll notice these don't have a constructor, hence why it is not necessary.
class MyOtherComponent extends React.Component {
render() {
return <div>Hi {this.props.name}</div>;
}
}
One point of confusion I've seen from some developers I've spoken to is that the components that have no constructor and therefore do not call super() anywhere, still have this.props available in the render() method. Remember that this rule and this need to create a this binding for the constructor only applies to the constructor.
When you pass props to super, the props get assigned to this. Take a look at the following scenario:
constructor(props) {
super();
console.log(this.props) //undefined
}
How ever when you do :
constructor(props) {
super(props);
console.log(this.props) //props will get logged.
}
When implementing the constructor() function inside a React component, super() is a requirement. Keep in mind that your MyComponent component is extending or borrowing functionality from the React.Component base class.
This base class has a constructor() function of its own that has some code inside of it, to setup our React component for us.
When we define a constructor() function inside our MyComponent class, we are essentially, overriding or replacing the constructor() function that is inside the React.Component class, but we still need to ensure that all the setup code inside of this constructor() function still gets called.
So to ensure that the React.Component’s constructor() function gets called, we call super(props). super(props) is a reference to the parents constructor() function, that’s all it is.
We have to add super(props) every single time we define a constructor() function inside a class-based component.
If we don’t we will see an error saying that we have to call super(props).
The entire reason for defining this constructor() funciton is to initialize our state object.
So in order to initialize our state object, underneath the super call I am going to write:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// React says we have to define render()
render() {
return <div>Hello world</div>;
}
};
So we have defined our constructor() method, initialized our state object by creating a JavaScript object, assigning a property or key/value pair to it, assigning the result of that to this.state. Now of course this is just an example here so I have not really assigned a key/value pair to the state object, its just an empty object.
Dan Abramov wrote an article on this topic:
Why Do We Write super(props)?
And the gist of it is that it's helpful to have a habit of passing it to avoid this scenario, that honestly, I don't see it unlikely to happen:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inside your code
class Button extends React.Component {
constructor(props) {
super(); // 😬 We forgot to pass props
console.log(props); // ✅ {}
console.log(this.props); // 😬 undefined
}
// ...
}
As per source code
function ReactComponent(props, context) {
this.props = props;
this.context = context;
}
you must pass props every time you have props and you don't put them into this.props manually.
super() is used to call the parent constructor.
super(props) would pass props to the parent constructor.
From your example, super(props) would call the React.Component constructor passing in props as the argument.
More information on super:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
For react version 16.6.3, we use super(props) to initialize state element name : this.props.name
constructor(props){
super(props);
}
state = {
name:this.props.name
//otherwise not defined
};
Here we won't get this in the constructor so it will return undefined, but we will be able to fetch this outside the constructor function
class MyComponent extends React.Component {
constructor() {
console.log(this); // Reference Error i.e return undefined
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
If we are using super(), then we can fetch the "this" variable inside the constructor as well
class MyComponent extends React.Component {
constructor() {
super();
console.log(this); // this logged to console
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
So when we are using super(); we will be able to fetch this but this.props will be undefined in the constructor. But other than constructor, this.props will not return undefined.
If we use super(props), then we can use this.props value inside the constructor as well
Sophie Alpert's Answer
If you want to use this.props in the constructor, you need to pass
props to super. Otherwise, it doesn’t matter because React sets .props
on the instance from the outside immediately after calling the
constructor.
Here is the fiddle I've made:jsfiddle.net. It shows that props are assigned not in the constructor by default. As I understand they are assinged in the method React.createElement. Hence super(props) should be called only when the superclass's constructor manually assings props to this.props. If you just extend the React.Component calling super(props) will do nothing with props. Maybe It will be changed in the next versions of React.
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);
I'm brand new to React and I'm using it in combination with ES6 classes. I have a class that inherits from React.Component and renders DOM based on a single property in its state. Here's how that looks:
class LoadingScreen extends React.Component {
constructor(props) {
super(props);
this.state = { state: 'isHidden' };
showTrying() {
this.setState({ state: 'isTrying' });
}
hideAll() {
this.setState({ state: 'isHidden' });
}
render() {
switch (this.state.state) {
case 'isHidden':
return null;
case 'isTrying':
// Returns a bunch of DOM elements
}
}
In a parent class, that is not a React component (I'm attempting to migrate existing code that uses no framework), I want to:
Create an instance of LoadingScreen
Call React.render to insert it in the DOM
Call methods such as hide or showTrying on that instance to update its state
I've tried:
this.loadingScreen = new LoadingScreen();
React.render(this.loadingScreen, mountNode); // React invalid component error
// Later on...
this.loadingScreen.showTrying();
And also tried:
this.loadingScreen = React.render(React.createElement("LoadingScreen"), mountNode);
// Later on...
this.loadingScreen.showTrying(); // Undefined is not a function
Clearly I'm missing something fundamental here. :)
It would be more common that you'd set a property on the LoadingScreen component instance to control the visibility of the internal representation rather than adjusting the state via a function call on the object instance.
class LoadingScreen extends React.Component {
constructor(props) {
super(props);
}
render() {
switch (this.props.mode) {
case 'isHidden':
return null;
case 'isTrying':
// Returns a bunch of DOM elements
}
}
}
LoadingScreen.propTypes = {
mode: React.PropTypes.string
};
LoadingScreen.defaultProps = {
mode: 'isTrying'
};
Then, from the parent, you'd do something like this for example:
var currentMode = "isTrying";
React.render(<LoadingScreen mode={ currentMode } />, mountNode);
Or, another pattern is that the parent container/component uses the value of the property (I've called mode) to just not create and render the LoadingScreen component at all.
If the LoadingScreen needs to, you could copy the property value into local state as you've done.
Your second approach was close.
The first argument to React.createElement can either be a string (div, span, etc.) or a subclass of React.Component. In your case, the first argument should be LoadingScreen.
this.loadingScreen = React.render(React.createElement(LoadingScreen), mountNode);
this.loadingScreen.showTrying();