React Javascript - Visual Studio Code doesn't auto-complete object properties - javascript

I have a React HOC that propagate an instance of a class to the children.
import React from "react";
import ObjContext from "../../context/Obj/ObjContext";
const withObj = (Component) => (props) => (
<ObjContext.Consumer>
{(obj) => <Component {...props} obj={obj} />}
</ObjContext.Consumer>
);
export default withObj;
Now, if in one of the child, I start coding, my code editor (VS Code Studio) doesn't display the properties of the object.
When I do props.obj. the editor doesn't show me all the stuff which is inside the object.
Instead, if I do const obj = new Obj() directly, I can see them.
Why is that? Is impossible to see the data which is inside the object that is propagated from a HOC and received via props?
Any workaround?
Thank you.

As said in the comments, the real answer here is TypeScript.
To see how your vs code editor would react with typescript, you can quickly do:
// Inside child component:
import Obj from 'path/to/obj'
...
export class ChildComponent extends React.Component {
this.obj: Obj;
constructor(props) {
super(props);
this.obj = this.pros.obj;
this.obj.something() // something would be proposed by ide
}
...
}
(Ofc the vs code will growl saying that types are not eligible in a .js file and you'd need .tsx instead, but for the purpose of taking a look at how TS would help with the auto-completion, its fine.

Related

Need to Know how the ComponentWillMount came from React Library

Need to know how we are accessing the Lifecycle methods from React :
import React, { Component } from "react";
class App extends Component{
constructor() {
super();
this.state = {};
}
componentWillMount() {
//Theoretically we tell that this `componentWillMount` is dereived/extended from 'Component' Class ie, in above like `class App extends Component`
}
render() {
return (
<div>
...
</div>
);
}
};
Now the question is if you open the react source code file from here https://unpkg.com/react#18.2.0/umd/react.development.js
you do not find any abstract method like for componentWillMount
But if you open the react-dom file, https://unpkg.com/react-dom#18.2.0/umd/react-dom.development.js you can find the method, Question is Since we extent the Class with React.Component, we assume it is inside React library, not in ReactDOM, So not Clear How we are accessing the LifeCycle hooks from React library, which infact not available in that library
The magic seems to be in react-reconciler:
You can find typeof instance.componentDidMount === 'function' instances in here that seem to mark that the React fiber is in a phase where it needs to have that function called.
The function is then finally called here in ReactFiberCommitWork.
How those functions get called is more and more involved, and quite asynchronous too, likely.

React Context API not working from custom NPM component library

I've built a ReactJS component library that I use for multiple projects installed via an NPM package using a sim link. I want to use the context API to pass data from a parent component served from the component library to my base project to be consumed by multiple consumer components also served from the component library. When I try the context is always undefined in my child components.
If I place my consumer component in my provider component within my library it works like a champ but this defeats what I'm trying to achieve. If I export both the provider and the consumer to my base project the consumer doesn't see the provider.
This is from my base project
import { Screen, COD, GenericSocketServer } from 'component-library'
export default class View extends React.PureComponent {
render() {
return (
<Screen className="screen odmb1">
<GenericSocketServer>
<COD />
</GenericSocketServer>
</Screen>
)
}
}
This is my provider code exported from my 'component-library'
import React from 'react';
import MyContext from "./context";
import COD from './../cod';
export default class GenericSocketServer extends React.Component {
render() {
return (
<MyContext.Provider value={{ foo: 'bar' }}>
<COD />
{this.props.children}
</MyContext.Provider>
);
}
}
This is my content code used in 'component-library'
import React from 'react'
const MyContext = React.createContext()
export default MyContext
This is my consumer component exported from 'component-library'
import MyContext from "../GenericSocketServer/context"
class COD extends React.Component {
render() {
return (
<React.Fragment>
<MyContext.Consumer>
{(context) => {
/*
context comes back undefined
I expect { foo: 'bar' }
*/
console.log('context :', context)
return (
<p>This should work</p>
)}}
</MyContext.Consumer>
</React.Fragment>
)
}
}
Context always comes back undefined as if it doesn't see the parent provider. I think I'm ether doing something wrong initializing the context myself or for some reason the two components I'm importing just don't share the same context. Please help!! Not sure if I should give up on this and just use redux.
Maybe you are making multiple instances of the component providing the context. Let's say you have a component Sound, which starts by:
const { Provider, Consumer } = React.createContext();
If you import this library from your main project, the context will be created at the global space. You then use it to render your document tree. But in another component you also imported this library, which had to be resolved during webpack transpilation. It thus has its own copy of the above lines and a context object created in its own space. The problem occurs when you try to use the Consumer, because the Provider was only made by the main project for the first context object, and the second context's provider instance was never instantiated, thus returns undefined.
A solution to the problem is to enforce a single context object, which you can achieve by telling the second component's webpack that the provider-owning library is an external, so when webpack reaches e.g. the "import sound" line, it will not go further and will assume this dependency is resolved at runtime. When runtime comes, it will take it from the same place where the main project is taking it. To do this in webpack, e.g. for above "sound" library, add this to your other component (not main project):
{
...
externals: {
...
'sound': 'sound'
}
...
}
Also in your component package.json:
{
...
peerDependencies: {
"sound": "^1.2.3"
}
}
Apart from Darko's answer, esm and cjs export is also a possible reason for context to fail in a package. If you use the hook in esm and the provider in cjs, you will not get the value for that context.
I recently had a similar issue where I was trying to consume the value of a context inside my library components but using the provider (imported from the package) in the host app.
I managed to solve the issue just by making react and react-dom external and peerDependencies when bundling in rollup.
should your code of consumer be
<React.Fragment>
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
</React.Fragment>
as stated from the official react doc : https://zh-hant.reactjs.org/docs/context.html
when you define
you can use it like

import or require React components dynamically

I'm trying to import / require components dynamically, but somehow when I do it React complains. The require function does find it, but React throws an error saying it is missing some functions 't' etc.. All of this in an electron app.
I have a wizard setup (that is working, but not so elegant I think), where each page has it's own layout and jsx component. If I'd like to add a new page, I don't want to manage x-number of files, and at the moment I have to due to the setup I have currently. Below you can find what I want to achieve and what I'm doing now to achieve it. If there are any suggestions, code smells or better options please let me know as I'm quite new to React and ES2015 (as I'm from a C# background).
What I'm trying to achieve
export default class WizardPageContainer extends Component {
// Constructor
constructor(props) {
super(props);
}
// Render
render() {
const WizardPage = require(`./path/to/${this.props.step.id}`);
return (
<WizardPage step={this.props.step} />
);
}
}
How I'm currently doing it : which means I have to declare the imports / files first on top of the "WizardPageContainer" component.. Which means extra work and prone to errors/forgetting things. I should add, this code is working now ok, but I don't think this is elegant/future proof:
/* PAGES */
import WizardPage_Welcome from './pages/0.wizard.welcome';
import WizardPage_SystemCheck from './pages/1.wizard.systemCheck';
import WizardPage_SignIn from './pages/2.wizard.signIn';
import WizardPage_ExamCode from './pages/3.wizard.examCode';
import WizardPage_TakeExamination from './pages/4.wizard.takeExamination';
import WizardPage_Close from './pages/5.wizard.close';
const pages = [
WizardPage_Welcome,
WizardPage_SystemCheck,
WizardPage_SignIn,
WizardPage_ExamCode,
WizardPage_TakeExamination,
WizardPage_Close
];
/*/********************************************************************///
/* ******************************************************************** */
/* COMPONENT */
export default class WizardPageContainer extends Component {
// Constructor
constructor(props) {
super(props);
}
// Render
render() {
const WizardPage = pages[`${this.props.step.id}`];
return (
<WizardPage step={this.props.step} />
);
}
}
/*/********************************************************************///
I think it is about the "default". i have problem like this. Can you check this code;
https://github.com/robeio/robe-react-admin/blob/master/src/app/HasAuthorization.jsx#L10
Also you can check the example usage;
https://github.com/robeio/robe-react-admin/blob/master/src/app/HasAuthorization.jsx#L26
Your const pages needs to be an object, not an array.
You can see a working version I made of this here:
https://github.com/Frazer/meteor-react-nav/blob/master/lib/jsx/App.jsx
Best advice: Use Webpack to handle your imports, it's way more efficient than we could ever be.

Rendering multiple react component from an array

I've a react component, Let's say component_1.
And, my array is of the form, let's say, array_list =[{},{},{},{}].
I'm trying to render this component inside my another component, component_2, like so:
import component_1 from 'component_1'
import array_list from 'array_list'
class component_2 extends Component{
constructor(props){
super(props)
}
render(){
const {renderMenu} = this.props
var contentData = [];
array_list.forEach(function(item, index, array){
contentData.push(<component_1 {...item} />);
});
return(
<div>
<div className="ui four column centered grid">
{contentData}
</div>
</div>
)
}
}
export default component_2
while, it generally works with other HTML elements. Here it throws an error:
React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of `OnBoardingContentElement`
Can render an array of react component's this way? If not, then is there any circumvent approach for this?
Are you sure your import statement is working? Also look into array.map, it is perfect for turning an array of object data into an array of components.
This is a follow up to #John's answer.
I ran into this exact problem last night. Here's what I was doing:
// MyComponent.js
export const MyComponent = (<h1>Hello, world</h1>);
// index.js
import { MyComponent } from './MyComponent';
React.render((<MyComponent />), document.getElementById('root'));
Can you see the error? It's kind of subtle.
The problem is that the MyComponent.js is NOT exporting a React component. It's exporting a React element that's already instantiated.
#John is suggesting that this might be what you're doing. The best way to correct it is the ensure that MyComponent.js is actually exporting a component:
// MyComponent.js
export const MyComponent = () => (<h1>Hello, world</h1>);

Construct React Component from a React Element

I'm trying to create a "higher-order" function in React that performs some permissions-based checks on the wrapped component and returns it accordingly.
MyComponent.js
...
export default Permissions(MyComponent)
Permissions.js
export default function Permissions(Component) {
class NewComponent extends React.Component {
// ... perform checks here
render() {
return {validPermissions && <Component />}
}
}
}
However, I'd like to be able to use this Permissions as a React Component (as opposed to a function that wraps the component export).
It would looks similar to this:
<Permissions>
<MyComponent />
</Permissions>
When I run React.Component.isPrototypeOf(Component.children) I get false in these instances. My inclination is to think that the solution is to use some React or ReactDOM method to transform the React Element into a React Component, and then perform the same checks.
How can I transform a React Element into a React Component?
Update:
I gave the bit about permissions as context, but not looking for help with regard to implementing permissions.
I am basically looking for the opposite of React.createElement(MyComponent).
You can use a functional component, which combines the best of both worlds: it's simple (just a function!) and at the same time it's a proper stateless React component.
const Permissions = ({ granted, children }) =>
granted ? React.Children.only(children) : null;
Usage:
<Permissions granted={true}>
<MyComponent />
</Permissions>

Categories

Resources