Modifying a React component with a user script - javascript

Short version
How can I modify a React component with a user script (e.g. code injected by Tampermonkey) without breaking the app when I don't have access to the original source?
Longer version
Modifying the DOM of a React component directly can break the app because the reconciliation algorithm can get confused by manual DOM changes. Hence, I would like to modify a React component in my user script so React can keep rendering unhindered.
In a simple setup, React discovers components in the global namespace, and we can modify a component easily (see this fiddle for a working example):
oldComponent = Component
function Component(props) {
// modify the props to change the appearance of the component
return oldComponent(modifiedProps);
}
However, the situation is more complex when the application is deployed because the components are typically bundled using webpack. How would I be able to reproduce the example above in an application whose components have been bundled?

Related

Sharing data across React components

In front-end apps there is often data which needs to be accessed by many components. Routing is one example, another is configuration data, e.g. feature switches, default language, etc.
In an app that isn't using any particular framework, I might share this configuration data across modules using
export class Configuration {
static getConfig () {
// get the config from the server
return axios.get('/config').then(function (response) {
return response;
})
}
}
Then import this class into any module that needs to access configuration data. With some front-end framework, it's obvious how to share such "global" data, e.g.
AngularJS - Configuration should be defined as a service that's dependency-injected into any controllers/directives/services that need to access config. data
Vue.js - use a mixin
However, when using ReactJS it's not obvious which approach should be used. Possible options are:
A plain-old JavaScript module. Encapsulate the data to be shared as a function/method, and import it into any React components that need to access it. The seems like the simplest approach, but I have the feeling that when writing a ReactJS app everything should be defined as a component, rather than JavaScript classes/functions.
Redux seems to be recommended approach for sharing-state within large apps, but this feels like overkill for smaller projects
Something else?
but I have the feeling that when writing a ReactJS app everything
should be defined as a component, rather than JavaScript
classes/functions.
I really don't see a reason why everything should be a component in React. If it is just data, you can create a single instance of that JS object say and import that anywhere you need it.
I have used similar thing in my app, where I had a "global" kind of object which was saving different configs etc, and then I was using that in the components which needed that data.
Here is also some more info about component communication in React.
A plain-old JavaScript module. Encapsulate the data to be shared as a function/method, and import it into any React components that need
to access it. The seems like the simplest approach, but I have the
feeling that when writing a ReactJS app everything should be defined
as a component, rather than JavaScript classes/functions.
I disagree, React is a library that helps to create user interfaces through components but it doesn't mean that (services, translations, configuration data) have to be built into components, on the other hand, it's actually discouraged you shouldn't couple your services/configuration to a library
you should limit the scope of React to what it is used for. So using plain-old JavaScript modules feels the right way to implement a simple react app.
Redux seems to be recommended approach for sharing-state within large
apps, but this feels like overkill for smaller projects
I think it depends on the complexity of the app rather the size, here is where you should think on, how does your app will evolve or if redux isn't what you really need to remove all this data-sharing dependency within React.
Something else?
The react context (discourage)
The observable pattern
https://www.npmjs.com/package/react-observable-subscribe
I think you should go for the redux solution. It sounds like an over kill but it has an added advantage of having a global state object, therefore you can easily choose when to re-render your app when data is shared across compoents.
you can use context in react :
https://reactjs.org/docs/context.html
or you can create a global variable in window object too
the other way is to use observer design pattern plagin and use it.
mobX or other stateManagement component is good too beside redux

How do I create a hook for React Router Dom without any third party libraries?

I need a hook for React Router whereby everytime a user navigates to page analytic data is sent to our server.
This needs to be on every page so I want to abstract this network call away from the developer so it all happends in the background. I don't want developers to have to manually put an analytic event on every Route they create. I need some kind of middleware for react router.
We are currently using the react-router-dom library. I looked through the source code but I could not find any methods to hook into events; or any notion of events at all.
I see their are react-router-hook but it looks like that package depends on the react-router package, which we don't use. And honestly I am not thrilled about introducing a new external library just for what should be simple behavior.
I suppose I could always just import the native Route object from react-router-dom and create a wrapper for it which would allow me to implement my own behavior. I would prefer not to extend a third party library unless it was absolutely necessary though.
How do I create a hook for React Router Dom without any third party libraries?
Wrapper is one option, you named it.
However, I would go for a hook in my top (<Application />) component to detect route changes and send events.
componentWillReceiveProps(nextProps) {
if (this.props.location !== nextProps.location) {
sendEvent(); // location has changed, do your work
}
}
PS. Depending on your setup, you might need to wrap in withRouter to make it aware of location changes.

React with Redux? What about the 'context' issue?

I normally post code related stuff on Stack, but this is more a question about what the general thoughts of the community are.
There seems to be a lot of people advocating the use Redux with React to manage data/state, but while reading and learning both I've come across something that doesn't quite look right.
Redux
At the bottom of this page: http://redux.js.org/docs/basics/UsageWithReact.html (Passing the Store) it recommends using the "Magic" of React 'Context'.
One option would be to pass it as a prop to every container component. However it gets tedious, as you have to wire store even through presentational components just because they happen to render a container deep in the component tree.
The option we recommend is to use a special React Redux component called to magically make the store available to all container components...
React
On the React Context page (https://facebook.github.io/react/docs/context.html) it has a warning at the top:
Context is an advanced and experimental feature. The API is likely to change in future releases.
Then at the bottom:
Just as global variables are best avoided when writing clear code, you should avoid using context in most cases...
Do not use context to pass your model data through components. Threading your data through the tree explicitly is much easier to understand...
So...
Redux recommends using the React 'Context' feature rather than passing the store along down to each component via 'props'. While React recommends the opposite.
Also, it seems that Dan Abramov (the creator of Redux) now works for Facebook (the creator of React), just to confuse me more.
Am I reading all this right..?
What is the general current consensus on this issue..?
Context is an advanced feature and is subject to change. In some cases its conveniences outweigh its downsides so some libraries like React Redux and React Router choose to rely on it despite the experimental nature.
The important part here is the word libraries. If context changes its behavior, we as library authors will need to adjust. However, as long as the library doesn’t ask you to directly use the context API, you as the user shouldn’t have to worry about changes to it.
React Redux uses context internally but it doesn’t expose this fact in the public API. So you should feel much safer using context via React Redux than directly because if it changes, the burden of updating the code will be on React Redux and not you.
Ultimately React Redux still supports always passing store as a prop so if you want to completely avoid context, you have that choice. However I would say this is impractical.
TLDR: Avoid using context directly unless you really know what you are doing. Using a library that happens to rely on context internally is relatively safe.
I don't know about others, but I prefer using react-redux's connect decorator to wrap my components so that only the props from the store I need are passed into my component. This justifies the use of context in a sense because I am not consuming it (and I know, as a rule, any code that I am in charge of will not consume it).
When I test my components, I test the non-wrapped component. Because react-redux only passed the props I needed on that component, I now know exactly what props I need when I'm writing the tests.
I suppose the point is, I don't ever see the word context in my code, I don't consume it, so to a certain degree, it doesn't affect me! This doesn't say anything about Facebook's "experimental" warning.. If context disappeared, I'd be just as screwed as everyone else until Redux was updated.
There's an npm module that makes it really easy to add redux to the react context
https://github.com/jamrizzi/redux-context-provider
https://www.npmjs.com/package/redux-context-provider
import React, { Component } from 'react';
import ReduxContextProvider from 'redux-context-provider';
import createStore from './createStore';
import actions from './actions';
import Routes from './routes';
export default class App extends Component {
render() {
return (
<ReduxContextProvider store={store} actions={actions}>
<Routes />
</ReduxContextProvider>
);
}
}
React ships with all the features you need to handle your state without a single additional library. Most of your application's states should not be global as they live just fine in a useState or useReducer or custom hook next to your components.
So before you dive into the world of advanced state management (e.g. Redux), consider using the tools React ships with out of the box.
If you are interested in learning a bit more about this, I'd recommend this article by Andy Fernandez, which dives into the details on Redux: Context API vs Redux

What is "Mounting" in React js?

I am hearing the term "mount" too many times while learning ReactJS. And there seem to be lifecycle methods and errors regarding this term. What exactly does React mean by mounting?
Examples: componentDidMount() and componentWillMount()
The main job of React is to figure out how to modify the DOM to match what the components want to be rendered on the screen.
React does so by "mounting" (adding nodes to the DOM), "unmounting" (removing them from the DOM), and "updating" (making changes to nodes already in the DOM).
How a React node is represented as a DOM node and where and when it appears in the DOM tree is managed by the top-level API. To get a better idea about what's going on, look at the most simple example possible:
// JSX version: let foo = <FooComponent />;
let foo = React.createElement(FooComponent);
So what is foo and what can you do with it? foo, at the moment, is a plain JavaScript object that looks roughly like this (simplified):
{
type: FooComponent,
props: {}
}
It's currently not anywhere on the page, i.e. it is not a DOM element, doesn't exist anywhere in the DOM tree and, aside from being React element node, has no other meaningful representation in the document. It just tells React what needs to be on the screen if this React element gets rendered. It is not "mounted" yet.
You can tell React to "mount" it into a DOM container by calling:
ReactDOM.render(foo, domContainer);
This tells React it's time to show foo on the page. React will create an instance of the FooComponent class and call its render method. Let's say it renders a <div />, in that case React will create a div DOM node for it, and insert it into the DOM container.
This process of creating instances and DOM nodes corresponding to React components, and inserting them into the DOM, is called mounting.
Note that normally you'd only call ReactDOM.render() to mount the root component(s). You don't need to manually "mount" the child components. Every time a parent component calls setState(), and its render method says a particular child should be rendered for the first time, React will automatically "mount" this child into its parent.
React is an isomorphic/universal framework. That means that there is a virtual representation of the UI component tree, and that is separate from the actual rendering that it outputs in the browser. From the documentation:
React is so fast because it never talks to the DOM directly. React maintains a fast in-memory representation of the DOM.
However, that in-memory representation is not tied directly to the DOM in the browser (even though it is called Virtual DOM, which is an unfortunate and confusing name for an universal apps framework), and it is just a DOM-like data-structure that represents all the UI components hierarchy and additional meta-data. Virtual DOM is just an implementation detail.
"We think the true foundations of React are simply ideas of components and elements: being able to describe what you want to render in a declarative way. These are the pieces shared by all of these different packages. The parts of React specific to certain rendering targets aren't usually what we think of when we think of React." - React js Blog
So, the conclusion is that React is Rendering agnostic, which means that it doesn't care about what is the final output. It can be a DOM Tree in the browser, it can be XML, Native components or JSON.
"As we look at packages like react-native, react-art, react-canvas, and react-three, it's become clear that the beauty and essence of React has nothing to do with browsers or the DOM." - React js Blog
Now, that you know how React works, it is easy to answer your question :)
Mounting is the process of outputting the virtual representation of a component into the final UI representation (e.g. DOM or Native Components).
In a browser that would mean outputting a React Element into an actual DOM element (e.g. an HTML div or li element) in the DOM tree. In a native application that would mean outputting a React element into a native component. You can also write your own renderer and output React components into JSON or XML or even XAML if you have the courage.
So, mounting/unmounting handlers are critical to a React application, because you can only be sure a component is output/rendered when it is mounted. However, the componentDidMount handler is invoked only when rendering to an actual UI representation (DOM or Native Components) but not if you are rendering to an HTML string on the server using renderToString, which makes sense, since the component is not actually mounted until it reaches the browser and executes in it.
And, yes, Mounting is also an unfortunate/confusing name, if you ask me. IMHO componentDidRender and componentWillRender would be much better names.
Mounting refers to the component in React (created DOM nodes) being attached to some part of the document. That's it!
Ignoring React you can think of these two native functions as mounting:
replaceChild
appendChild
Which are likely the most common functions React uses to mount internally.
Think of:
componentWillMount === before-mount
And:
componentDidMount === after-mount
https://facebook.github.io/react/docs/tutorial.html
Here, componentDidMount is a method called automatically by React when a component is rendered.
The concept is that you're telling ReactJS, "please take this thing, this comment box or spinning image or whatever it is I want on the browser page, and go ahead and actually put it on the browser page. When that's done, call my function that I've bound to componentDidMount so I can proceed."
componentWillMount is the opposite. It will fire immediately BEFORE your component renders.
See also here
https://facebook.github.io/react/docs/component-specs.html
Finally, the "mount" term seems to be unique to react.js. I don't think it is a general javascript concept, or even a general browser concept.
Mounting refers to the initial page loading when your React component is first rendered. From React documentation for Mounting: componentDidMount:
Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. At this point in the lifecycle, the component has a DOM representation which you can access via React.findDOMNode(this).
You can contrast this with componentDidUpdate function, which is called everytime that React renders (except for the initial mount).
The main goal of React js is to create reusable components. Here, components are the individual parts of a webpage. For example, in a webpage the header is a component, the footer is a component, a toast notification is a component and etc. The term "mount" tells us that these components are loaded or rendered in the DOM. These are many top-level APIs and methods dealing with this.
To make it simple, mounted means the component has been loaded to the DOM and unmounted means the components has been removed from the DOM.

Initializers in React.js?

I am using react 0.13.1 in a rails 4.2.1 application with react-rails and browserify-rails. I am not doing a single page application, but instead adding react components on rendered pages and using the rails routing system.
When I load a page, I want to initialize a few things, but I'm not sure where this code should go. Does react have a initializer function that it always calls before rendering components or does it always render the first component first such that I can just add initializer code to the first component?
How would I go about putting react initializer code in my multi page rails application? (should work for client or server rendered react code)
Note: Assume only react and plain javascript is being used.
Edit: not asking about initializing on each component, but rather initializing code after page load, but before react starts going through and rendering any components.
The entry point to initialize React is the call to React.render(), so you should perform any initialization before that call. If the initialization is async, just call React.render() in the callback.
And there's also the componentWillMount method on every component which can be used for component specific initialization.
Yeah it's all there in React's documentation. Each component has a getInitialState which is called when the object is first requested, you then have oncompentdidmount and another for when the component is dismounted. It's all JavaScript code so initialization code can be done inside each component or just declared globally before or after the React code is executed.
I have a similar setup with Django, where I use Django for it's strengths. Routing, Sessions, Authentication, Rest API ect... and have Django render a bar bones template which loads the React UI. This process seems to work really well, it's greatly sped up my site from where it was before, and the UI is awesome.
So in one particular case with my django app I need certain data to be initialized before the React components try to render themselves. therefore I wrap the initialization code into a function which is called on page load, then as a callback after that function has completed I call another function that actually add's my react components to the page. From there each component has it's own initialization which you've said you're not interested in at the moment, but that is how I handled the situation.

Categories

Resources