React: How to add new value to props from imported file? - javascript

I am (very) new to React and have been given the task of adding some data to a component that's being brought in from another file. This file spits out some JSON and I want to access certain pieces of data from it, for example:
config.forms.enquiry.title
I am importing the file fine - no problems there. But I am not sure how to include config into my props.
I found a working example, in another file, and have copied what it does. My code is as such
Brings in file with JSON:
import { withSettings } from 'services/settingsFile';
Add config in render function:
render () {
const styles = getStyles(this.props, this.context, this.state);
const { config } = this.props;
// other stuff
Add to propTypes:
enquiryForm.propTypes = {
config: PropTypes.object.isRequired,
// other stuff
Add to compose:
export const enquiryForm = compose(
withSettings,
// other stuff
However, I get the error:
Failed context type: The context config is marked as required in
n, but its value is undefined.
And from here I am not sure what to do. I know it's a tough question, but I know very little about React and have been thrown in the deep end.
Would anyone know what/where I should be searching for to fix this?

If you can import it like,
import { withSettings } from 'services/settingsFile';
why dont you use it like,
const { config } = withSettings;

OK, so the issue was that there was no wrapping element setting config as am attribute.
I had to go up a level to where my component was being brought in and wrap:
<SettingsFile config={window.settingsFile}>
around:
<Component conf={config} />
Then, the component I was working on was able to read config.

Related

Is it possible to create a custom esLint rule that would warn users on using a particular component?

We use Bootstrap-vue in our project, but we have a few files that extend the basic bootstrap component, e.g. <form-group> rather than Bootstrap-Vue's provided <b-form-group>.
I want to ensure that users use the <form-group> element only, and I don't want to have to read through PRs to see if they've accidentally forgotten. Is it possible to create an ES Lint rule that would flag up to a user that they should use <form-group> rather than <b-form-group>?
Thanks in advance!
You could overwrite the bootstrap component to throw when used?
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
Vue.use(BootstrapVue);
const Base = Vue.options.components["b-form-group"];
const DeprecatedForm = Base.extend({
methods: {
beforeCreate() {
throw new Error("[DEPRECATED] 'b-form-group' should not be used, prefer using 'form-group'");
},
})
}
Vue.component('b-form-group', DeprecatedForm );
Unfortunatly this does not answer the request of using ESLint for this. But it will ultimatly prevent the use of the component.
Another way could be to simply replace the bootstrap component with yours so that both can be used, but this may lead to confusion... You'd do this:
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
Vue.use(BootstrapVue);
const Base = Vue.options.components["form-group"];
// I think we need to create a new component, maybe not?
const Form= Base.extend({});
Vue.component('b-form-group', DeprecatedForm );

How to import lodash library in js file [duplicate]

I'd like to import this javascript package in React
<script src="https://cdn.dwolla.com/1/dwolla.js"></script>
However, there is no NPM package, so I can't import it as such:
import dwolla from 'dwolla'
or
import dwolla from 'https://cdn.dwolla.com/1/dwolla.js'
so whenver I try
dwolla.configure(...)
I get an error saying that dwolla is undefined. How do I solve this?
Thanks
Go to the index.html file and import the script
<script src="https://cdn.dwolla.com/1/dwolla.js"></script>
Then, in the file where dwolla is being imported, set it to a variable
const dwolla = window.dwolla;
This question is getting older, but I found a nice way to approach this using the react-helmet library which I feel is more idiomatic to the way React works. I used it today to solve a problem similar to your Dwolla question:
import React from "react";
import Helmet from "react-helmet";
export class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
myExternalLib: null
};
this.handleScriptInject = this.handleScriptInject.bind(this);
}
handleScriptInject({ scriptTags }) {
if (scriptTags) {
const scriptTag = scriptTags[0];
scriptTag.onload = () => {
// I don't really like referencing window.
console.log(`myExternalLib loaded!`, window.myExternalLib);
this.setState({
myExternalLib: window.myExternalLib
});
};
}
}
render() {
return (<div>
{/* Load the myExternalLib.js library. */}
<Helmet
script={[{ src: "https://someexternaldomain.com/myExternalLib.js" }]}
// Helmet doesn't support `onload` in script objects so we have to hack in our own
onChangeClientState={(newState, addedTags) => this.handleScriptInject(addedTags)}
/>
<div>
{this.state.myExternalLib !== null
? "We can display any UI/whatever depending on myExternalLib without worrying about null references and race conditions."
: "myExternalLib is loading..."}
</div>
</div>);
}
}
The use of this.state means that React will automatically be watching the value of myExternalLib and update the DOM appropriately.
Credit: https://github.com/nfl/react-helmet/issues/146#issuecomment-271552211
for typescript developers
const newWindowObject = window as any; // cast it with any type
let pushNotification = newWindowObject.OneSignal; // now OneSignal object will be accessible in typescript without error
You can't require or import modules from a URL.
ES6: import module from URL
What you can do is make an HTTP request to get the script content & execute it, as in the answer for how to require from URL in Node.js
But this would be a bad solution since your code compilation would depend on an external HTTP call.
A good solution would be to download the file into your codebase and import it from there.
You could commit the file to git if the file doesn't change much & are allowed to do it. Otherwise, a build step could download the file.
var _loaded = {};
function addScript(url) {
if (!loaded[url]) {
var s = document.createElement('script');
s.src = url;
document.head.appendChild(s);
_loaded[url] = true;
}
}
how to load javascript file from cdn server in a component
Add the script tag in your index.html and if you are using Webpack, you can use this webpack plugin https://webpack.js.org/plugins/provide-plugin/

Next js named imports with no SSR

I need to convert this line to next.js dynamic import and also without SSR
import { widget } from "./charting_library/charting_library";
I have tried this one
const widget = dynamic(() => import("./charting_library/charting_library").then((mod) => mod.widget), {
ssr: false
});
This seems not the correct way and also charting_libray.js file is a compiled js file in a previous project.
Is the problem is my importing method or the js file? If this is importing method how do I fix this?
const { widget } = await import("./charting_library/charting_library")
Maybe something along those lines might work? As for the SSR side I am not sure if you would need to execute it within a useEffect.

How can i set variables that i can access globally throught my app? [duplicate]

I initialized i18n translation object once in a component (a first component that loads in the app ). That same object is required In all other components. I don't want to re-initialize it in every component. What's the way around? Making it available to window scope doesn't help as I need to use it in the render() method.
Please suggest a generic solution for these problems and not i18n specific solution.
Beyond React
You might not be aware that an import is global already. If you export an object (singleton) it is then globally accessible as an import statement and it can also be modified globally.
If you want to initialize something globally but ensure its only modified once, you can use this singleton approach that initially has modifiable properties but then you can use Object.freeze after its first use to ensure its immutable in your init scenario.
const myInitObject = {}
export default myInitObject
then in your init method referencing it:
import myInitObject from './myInitObject'
myInitObject.someProp = 'i am about to get cold'
Object.freeze(myInitObject)
The myInitObject will still be global as it can be referenced anywhere as an import but will remain frozen and throw if anyone attempts to modify it.
Example of react state using singleton
https://codesandbox.io/s/adoring-architecture-ru3vt
(see UserContext.tsx)
If using react-create-app
(what I was looking for actually) In this scenario you can also initialize global objects cleanly when referencing environment variables.
Creating a .env file at the root of your project with prefixed REACT_APP_ variables inside does quite nicely. You can reference within your JS and JSX process.env.REACT_APP_SOME_VAR as you need AND it's immutable by design.
This avoids having to set window.myVar = %REACT_APP_MY_VAR% in HTML.
See more useful details about this from Facebook directly:
https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables
Why don't you try using Context?
You can declare a global context variable in any of the parent components and this variable will be accessible across the component tree by this.context.varname. You only have to specify childContextTypes and getChildContext in the parent component and thereafter you can use/modify this from any component by just specifying contextTypes in the child component.
However, please take a note of this as mentioned in docs:
Just as global variables are best avoided when writing clear code, you should avoid using context in most cases. In particular, think twice before using it to "save typing" and using it instead of passing explicit props.
Create a file named "config.js" in ./src folder with this content:
module.exports = global.config = {
i18n: {
welcome: {
en: "Welcome",
fa: "خوش آمدید"
}
// rest of your translation object
}
// other global config variables you wish
};
In your main file "index.js" put this line:
import './config';
Everywhere you need your object use this:
global.config.i18n.welcome.en
Is not recommended but.... you can use componentWillMount from your app class to add your global variables trough it... a bit like so:
componentWillMount: function () {
window.MyVars = {
ajax: require('../helpers/ajax.jsx'),
utils: require('../helpers/utils.jsx')
};
}
still consider this a hack... but it will get your job done
btw componentWillMount executes once before rendering, see more here:
https://reactjs.org/docs/react-component.html#mounting-componentwillmount
Here is a modern approach, using globalThis, we took for our React Native app.
globalThis is now included in...
Modern browsers - MDN documentation
Typescript 3.4 - Handbook documentation
ESLint v7 - Release notes
appGlobals.ts
// define our parent property accessible via globalThis. Also apply the TypeScript type.
var app: globalAppVariables;
// define the child properties and their types.
type globalAppVariables = {
messageLimit: number;
// more can go here.
};
// set the values.
globalThis.app = {
messageLimit: 10,
// more can go here.
};
// Freeze so these can only be defined in this file.
Object.freeze(globalThis.app);
App.tsx (our main entry point file)
import './appGlobals'
// other code
anyWhereElseInTheApp.tsx
const chatGroupQuery = useQuery(GET_CHAT_GROUP_WITH_MESSAGES_BY_ID, {
variables: {
chatGroupId,
currentUserId: me.id,
messageLimit: globalThis.app.messageLimit, // 👈 used here.
},
});
Can keep global variables in webpack i.e. in webpack.config.js
externals: {
'config': JSON.stringify({ GLOBAL_VARIABLE: "global var value" })
}
In js module can read like
var config = require('config')
var GLOBAL_VARIABLE = config.GLOBAL_VARIABLE
Hope this will help.
The best way I have found so far is to use React Context but to isolate it inside a high order provider component.
Maybe it's using a sledge-hammer to crack a nut, but using environment variables (with Dotenv https://www.npmjs.com/package/dotenv) you can also provide values throughout your React app. And that without any overhead code where they are used.
I came here because I found that some of the variables defined in my env files where static throughout the different envs, so I searched for a way to move them out of the env files. But honestly I don't like any of the alternatives I found here. I don't want to set up and use a context everytime I need those values.
I am not experienced when it comes to environments, so please, if there is a downside to this approach, let me know.
Create a file :
import React from "react";
const AppContext = {};
export default AppContext;
then in App.js, update the value
import AppContext from './AppContext';
AppContext.username = uname.value;
Now if you want the username to be used in another screen:
import AppContext from './AppContext';
AppContext.username to be used for accessing it.
For only declaring something, try this. Make sure MyObj is assigned at the proper time for you want to access it in render(), many ways was published before this thread. Maybe one of the simplest ways if undefined then create it does the job.
declare global {
interface Window {
MyObj: any;
}
}
USE CUSTOM HOOKS
It is very simple if you use custom hooks
Refer this link
https://stackoverflow.com/a/73678597/19969598
Full sample usage is available in the above post
This answer is for global part of question not I18N.
I wanted a global variable and function across all components of my application and without child-parent relationship.
This Answer is like a good one; but it was not completely clear to me so i had to test it my way.
I used below approach; not sure if this is a "good or bad practice" or even "off-topic"; but share in case help someone.
Global.jsx
const Global = () => { }
export default Global;
Global.var = 100;
Global.func = () => {
Global.var += 1;
alert(Global.var);
}
MyComponent1.jsx
import Global from "./Global";
import React from "react";
const MyComponent1 = () => {
return ( <h1 onClick={Global.func}>COM1: {Global.var}</h1>)
}
export default MyComponent1;
MyComponent2.jsx
import Global from "./Global";
import React from "react";
const MyComponent2 = () => {
return ( <h1 onClick={Global.func}>COM2: {Global.var}</h1>)
}
export default MyComponent2;
And anywhere like index.js
root.render(
<div>
.
.
.
<MyComponent1/>
<MyComponent1/>
<MyComponent2/>
<MyComponent2/>
.
.
.
</div>
);
Note: This way you have access to a global function or variable; but provided sample cannot update (render) screen itself cause no state or prop has been changed.
We can change the solution like this and keep ref of our components or DOM objects in our Global Zone like this (Not that i do not know its a good practice or even the worst case; so its on your own):
Global.jsx
const Global = () => { }
export default Global;
Global.var = 100;
Global.refs = [];
Global.inc = () => {
Global.var += 1;
Global.refs.forEach(ref => {
ref.current.innerText = Global.var;
});
}
MyComponent1.jsx, MyComponent2.jsx, ...
import Global from "./Global";
import React, { createRef } from "react";
const MyComponent1 = () => {
const ref = createRef();
Global.refs.push(ref);
return (<div onClick={Global.inc}>
<h2>COM1:</h2>
<h3 ref={ref} >{Global.var}</h3>
</div>);
};
export default MyComponent1;
I don't know what they're trying to say with this "React Context" stuff - they're talking Greek, to me, but here's how I did it:
Carrying values between functions, on the same page
In your constructor, bind your setter:
this.setSomeVariable = this.setSomeVariable.bind(this);
Then declare a function just below your constructor:
setSomeVariable(propertyTextToAdd) {
this.setState({
myProperty: propertyTextToAdd
});
}
When you want to set it, call this.setSomeVariable("some value");
(You might even be able to get away with this.state.myProperty = "some value";)
When you want to get it, call var myProp = this.state.myProperty;
Using alert(myProp); should give you some value .
Extra scaffolding method to carry values across pages/components
You can assign a model to this (technically this.stores), so you can then reference it with this.state:
import Reflux from 'reflux'
import Actions from '~/actions/actions'
class YourForm extends Reflux.Store
{
constructor()
{
super();
this.state = {
someGlobalVariable: '',
};
this.listenables = Actions;
this.baseState = {
someGlobalVariable: '',
};
}
onUpdateFields(name, value) {
this.setState({
[name]: value,
});
}
onResetFields() {
this.setState({
someGlobalVariable: '',
});
}
}
const reqformdata = new YourForm
export default reqformdata
Save this to a folder called stores as yourForm.jsx.
Then you can do this in another page:
import React from 'react'
import Reflux from 'reflux'
import {Form} from 'reactstrap'
import YourForm from '~/stores/yourForm.jsx'
Reflux.defineReact(React)
class SomePage extends Reflux.Component {
constructor(props) {
super(props);
this.state = {
someLocalVariable: '',
}
this.stores = [
YourForm,
]
}
render() {
const myVar = this.state.someGlobalVariable;
return (
<Form>
<div>{myVar}</div>
</Form>
)
}
}
export default SomePage
If you had set this.state.someGlobalVariable in another component using a function like:
setSomeVariable(propertyTextToAdd) {
this.setState({
myGlobalVariable: propertyTextToAdd
});
}
that you bind in the constructor with:
this.setSomeVariable = this.setSomeVariable.bind(this);
the value in propertyTextToAdd would be displayed in SomePage using the code shown above.

Next.js: How to dynamically import external client-side only React components into Server-Side-Rendering apps developed?

I know this question has been asked multiple times before but none of the solution seems to work.
I'm trying to use the library 'react-chat-popup' which only renders on client side in a SSR app.(built using next.js framework) The normal way to use this library is to call import {Chat} from 'react-chat-popup' and then render it directly as <Chat/>.
The solution I have found for SSR apps is to check if typedef of window !=== 'undefined' in the componentDidMount method before dynamically importing the library as importing the library normally alone would already cause the window is not defined error. So I found the link https://github.com/zeit/next.js/issues/2940 which suggested the following:
Chat = dynamic(import('react-chat-popup').then(m => {
const {Foo} = m;
Foo.__webpackChunkName = m.__webpackChunkName;
return Foo;
}));
However, my foo object becomes null when I do this. When I print out the m object in the callback, i get {"__webpackChunkName":"react_chat_popup_6445a148970fe64a2d707d15c41abb03"} How do I properly import the library and start using the <Chat/> element in this case?
Next js now has its own way of doing dynamic imports with no SSR.
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
Here is the link of their docs: next js
I've managed to resolve this by first declaring a variable at the top:
let Chat = ''
then doing the import this way in componentDidMount:
async componentDidMount(){
let result = await import('react-chat-popup')
Chat = result.Chat
this.setState({
appIsMounted: true
})
}
and finally render it like this:
<NoSSR>
{this.state.appIsMounted? <Chat/> : null}
</NoSSR>
You may not always want to include a module on server-side. For
example, when the module includes a library that only works in the
browser.
Import the library normally in child component and import that component dynamically on parent component.
https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr
This approach worked for me.

Categories