react-redux subscribe to store outside of component - reactjs

I am woking on localization for a react app, and for maintainability I want to define all localized text in separate files outside of the component. I have a localizationReducer which controls the selected locale - so far I've been able to work with useSelector and useDispatch hooks to interact with the reducer/store, but I'm wondering if I can subscribe to the store from a separate file which is not a react component. Here is an example of what I'm trying to do.
//Component which needs the localized text
import * as localizedText from "#localization/modules/fundsMovementTemplates"
<
<div>
<DropDown
extraClass={"localizationSelector"}
defaultValue={""}
value={localeFromStore}
listItems={localesDidNotLoad.current ? [localeOptions[0]] : localeOptions}
handleChange={(e) => updateLocale(e.target.value)}
/>
<span>{localizedText.pageTitle}</span>
</div>
//separate file with localized text
import translate from "#localization/translate"
import intl from "react-intl-universal"
export const titleText= translate(
intl,
"HOMEPAGE.labels.TITLETEXT.title",
initComplete,
"Page Title"
)
//Translate function that interacts with reducer
/*
This method wraps intl.get(), allowing for the loading of locale data asynchronously
(in LocalizationDropDown) and avoiding empty/null locale data errors/warnings.
Params (ALL REQUIRED):
1) intl: the instance of the react-intl-universal singleton
2) selector: the key of the locale value (e.g. 'RULE.title')
3) initComplete: boolean indicating the init status of the intl instance
4) defaultText: fallback in case the locale data doesn't load, etc.
*/
export const translate = (intl, selector, initComplete, defaultText) => {
if (initComplete) {
return intl.get(selector).defaultMessage(defaultText)
}
return defaultText
}
How can I subscribe a simple file like this to the store so I can import the localized text into another component?

Related

Passing properties from DropzoneArea to Dropzone

I am using DropzoneArea:
import { DropzoneArea } from 'material-ui-dropzone';
which is based on react-dropzone.
Dropzone from:
import Dropzone from 'react-dropzone'
contains certain props, not exposed by DropzoneArea but available by Dropzone
For instance disabled.
I have several question regarding it:
When I create DropzoneArea component, is there a way to mark it as disabled?
Is there a way to pass original properties of Dropzone
And here is an original issue I try to solve, probably the solution is not the best one and there are alternatives:
When a user uploads a file to DropzoneArea, I can send it to the server via onChange handler. I'd like to disable the whole component, while server is processing a file, until we get a response back.
The DropzoneArea has the property dropzoneProps, which is an object that is being passed (as props) to the Dropzone:
<DropzoneArea
dropzoneProps={ { disabled: true} }
...
/>

How to get Material UI Icon SVG path? React Icon Picker

I have a react application using Material UI's Icons. The main objective is to have an Icon Picker, where a user can select a MUI Icon and that selected icon's SVG path is saved within the state.
This svg path will then be saved out to an API where it can be displayed in various places. etc.
I've searched through documentation on MUI's site regarding icons, but it's all about implementation, which I can do just fine. I've looked for an npm package, without much luck.
I did come across this package (https://github.com/DMDc0de/material-ui-icon-picker), which is essentially what I'd like the picker to be - but it outputs the icon's name for an icon component <i />. Not what I want. I need the source of the SVG path.
Any direction towards this would be super helpful.
Go to the icon site: https://material.io/tools/icons/
Click on an icon
Click on "Selected icon" button (bottom left)
Click on the "SVG" button to download the SVG version
Alternatively, go to the GitHub repo and download the SVGs there.
One way of doing that programmatically is to load the component and to render it in a string. Then to extract the path from the string.
To do so, we can use the renderToString or renderToMarkupString method of ReactDomServer.
Than we can extract the path from the generated string. We can either parse the svg XML with the DOMParser or with a regexp.
Here's an example in TypeScript:
import EditIcon from '#material-ui/icons/Edit';
import ReactDOMServer from 'react-dom/server';
export function getEditIconPath(): string {
const iconString = ReactDOMServer.renderToString(<EditIcon />);
const parser = new DOMParser();
const svgDoc = parser.parseFromString(iconString, 'image/svg+xml');
const iconPath = svgDoc.querySelector('path')?.getAttribute('d') as string;
return iconPath;
}
Another way to achieve that would be to use the React Test Renderer, thus we can get directly a json including the different properties (including the path). However, it looks like this method is around 10 times slower than the previous method.
Here's an example with the second method:
import EditIcon from '#material-ui/icons/Edit';
import TestRenderer from 'react-test-renderer'; // ES6
export function getEditIconPath(): string {
const iconComponent = TestRenderer.create(<EditIcon />);
const iconJson = iconComponent.toJSON();
const path = iconJson.children[0].props.d;
return path;
}

React - pass props/state to rendered HTML string?

In my React app, I have stored a text template as an HTML string on the server which is passed up to the client to be rendered. Is it possible to render this HTML string in such a way that it can access it's parent component state/props? I've tried using dangerouslySetInnerHTML and ReactDOMServer.renderToString(), both of which render the string as HTML, but render {props.text} as a literal string, rather than a reference to the component's props.
For example, on the server I have stored:
<div>Welcome to my app</div>
<div>{props.message}</div>
<div>Have fun</div>
And the component
import React from "react"
import { connect } from "react-redux"
const mapStateToProps = (state) => {
return { state }
},
WelomeBody = (props) => {
return (
<div dangerouslySetInnerHTML={{"__html": props.state.welcomeMessageBody}} />
)
}
export default connect(mapStateToProps, null)(WelomeBody)
but this results in:
Welcome to my app
{props.message}
Have fun
Is there a way that this can be rendered so as to access the value of props.message, rather than just rendering it literally?
If what you have in mind is to send down a React component (in JSX syntax) from your server to your client and have the client somehow rehydrate/compile it into an actual, working component, this is not achievable that easily.
When you build and bundle your React app, only components that are statically referenced/imported in your application at compile time can be used at runtime on the browser.
So, in order to dynamically render a component from a template and embed variables into it, your choices are:
Render it into final HTML on your server, send down that HTML and have a simple React component perform dangerouslySetInnerHTML for it. But, like you've already observed, that content has to be the full HTML code, no templates, variables, JSX, etc. Everything is string and HTML at this point, no more processing.
Alternatively, send down a Markdown document and have a Markdown component parse it into HTML and display it.
Create a sophisticated component that can receive a string, parse it, tokenize it, substitute values in it, etc. Essentially, create your own template-processing component that can read a template (the string sent down from your server) and embed the appropriate values into it.
A simple solution (to substitute values into a template) would be:
function Filler(props) {
let html = props.template;
Object.keys(props).forEach(key => {
if (key !== "template")
html = html.replace("{" + key + "}", props[key]);
});
return <div dangerouslySetInnerHTML={{__html: html}} />;
}
and use it like
<Filler template={"<h1>Hello, {firstname} {lastname}</h1>"} firstname="John" lastname="Doe" />
But again, this is far from a full-fledged actual React component.

Redux Form converting payload from Number to String on blur

I'm trying to isolate why React/Redux/Redux-Form is automatically converting my payload to a string, whenever I focus and blur a input field.
There must be some process that is interfering but I don't know how to troubleshoot.
I've tried using the ReduxDevTools Chrome Extension which basically outputs :
type(pin): "##redux-form/INITIALIZE"
▶meta(pin)
▶payload(pin)
Amount(pin): 2222
type(pin): "##redux-form/FOCUS"
▶meta(pin)
type(pin): "##redux-form/BLUR"
▶meta(pin)
touch(pin): true
payload(pin): "2222.00"
Do you need to use redux-form?
If your data can remain within the context of the component, it should.
Here is one option, provided you don't need your data in the global app state:
import React, { Component } from 'react';
import wrapStateHelpers from 'react-state-helpers';
class Example extends Component {
render() {
const { mut, values: { someNumber } } = this.props;
return (
<input
type='number'
value={someNumber}
onChange={mut('someNumber', parseInt)} />
);
}
}
export default wrapStateHelpers(Example);
This just provides an event handler and wrapping component that manages form state in the component-level-state and then passes that state as the values object in the props to your component.
Note the parseInt passed to mut. Input fields nearly always return a string, and react-state-helpers provides an api for data conversion (second parameter to mut can be any function)
Here is a link to the project / docs, if this
The answer in this case was painfully obvious in retrospect. I was using normalize to format the value using .toFixed(2) which unknown to me, always returns a string.

Having services in React application

I'm coming from the angular world where I could extract logic to a service/factory and consume them in my controllers.
I'm trying to understand how can I achieve the same in a React application.
Let's say that I have a component that validates user's password input (it's strength). It's logic is pretty complex hence I don't want to write it in the component it self.
Where should I write this logic? In a store if I'm using flux? Or is there a better option?
The issue becomes extremely simple when you realize that an Angular service is just an object which delivers a set of context-independent methods. It's just the Angular DI mechanism which makes it look more complicated. The DI is useful as it takes care of creating and maintaining instances for you but you don't really need it.
Consider a popular AJAX library named axios (which you've probably heard of):
import axios from "axios";
axios.post(...);
Doesn't it behave as a service? It provides a set of methods responsible for some specific logic and is independent from the main code.
Your example case was about creating an isolated set of methods for validating your inputs (e.g. checking the password strength). Some suggested to put these methods inside the components which for me is clearly an anti-pattern. What if the validation involves making and processing XHR backend calls or doing complex calculations? Would you mix this logic with mouse click handlers and other UI specific stuff? Nonsense. The same with the container/HOC approach. Wrapping your component just for adding a method which will check whether the value has a digit in it? Come on.
I would just create a new file named say 'ValidationService.js' and organize it as follows:
const ValidationService = {
firstValidationMethod: function(value) {
//inspect the value
},
secondValidationMethod: function(value) {
//inspect the value
}
};
export default ValidationService;
Then in your component:
import ValidationService from "./services/ValidationService.js";
...
//inside the component
yourInputChangeHandler(event) {
if(!ValidationService.firstValidationMethod(event.target.value) {
//show a validation warning
return false;
}
//proceed
}
Use this service from anywhere you want. If the validation rules change you need to focus on the ValidationService.js file only.
You may need a more complicated service which depends on other services. In this case your service file may return a class constructor instead of a static object so you can create an instance of the object by yourself in the component. You may also consider implementing a simple singleton for making sure that there is always only one instance of the service object in use across the entire application.
The first answer doesn't reflect the current Container vs Presenter paradigm.
If you need to do something, like validate a password, you'd likely have a function that does it. You'd be passing that function to your reusable view as a prop.
Containers
So, the correct way to do it is to write a ValidatorContainer, which will have that function as a property, and wrap the form in it, passing the right props in to the child. When it comes to your view, your validator container wraps your view and the view consumes the containers logic.
Validation could be all done in the container's properties, but it you're using a 3rd party validator, or any simple validation service, you can use the service as a property of the container component and use it in the container's methods. I've done this for restful components and it works very well.
Providers
If there's a bit more configuration necessary, you can use a Provider/Consumer model. A provider is a high level component that wraps somewhere close to and underneath the top application object (the one you mount) and supplies a part of itself, or a property configured in the top layer, to the context API. I then set my container elements to consume the context.
The parent/child context relations don't have to be near each other, just the child has to be descended in some way. Redux stores and the React Router function in this way. I've used it to provide a root restful context for my rest containers (if I don't provide my own).
(note: the context API is marked experimental in the docs, but I don't think it is any more, considering what's using it).
//An example of a Provider component, takes a preconfigured restful.js
//object and makes it available anywhere in the application
export default class RestfulProvider extends React.Component {
constructor(props){
super(props);
if(!("restful" in props)){
throw Error("Restful service must be provided");
}
}
getChildContext(){
return {
api: this.props.restful
};
}
render() {
return this.props.children;
}
}
RestfulProvider.childContextTypes = {
api: React.PropTypes.object
};
Middleware
A further way I haven't tried, but seen used, is to use middleware in conjunction with Redux. You define your service object outside the application, or at least, higher than the redux store. During store creation, you inject the service into the middleware and the middleware handles any actions that affect the service.
In this way, I could inject my restful.js object into the middleware and replace my container methods with independent actions. I'd still need a container component to provide the actions to the form view layer, but connect() and mapDispatchToProps have me covered there.
The new v4 react-router-redux uses this method to impact the state of the history, for example.
//Example middleware from react-router-redux
//History is our service here and actions change it.
import { CALL_HISTORY_METHOD } from './actions'
/**
* This middleware captures CALL_HISTORY_METHOD actions to redirect to the
* provided history object. This will prevent these actions from reaching your
* reducer or any middleware that comes after this one.
*/
export default function routerMiddleware(history) {
return () => next => action => {
if (action.type !== CALL_HISTORY_METHOD) {
return next(action)
}
const { payload: { method, args } } = action
history[method](...args)
}
}
I needed some formatting logic to be shared across multiple components and as an Angular developer also naturally leaned towards a service.
I shared the logic by putting it in a separate file
function format(input) {
//convert input to output
return output;
}
module.exports = {
format: format
};
and then imported it as a module
import formatter from '../services/formatter.service';
//then in component
render() {
return formatter.format(this.props.data);
}
Keep in mind that the purpose of React is to better couple things that logically should be coupled. If you're designing a complicated "validate password" method, where should it be coupled?
Well you're going to need to use it every time the user needs to input a new password. This could be on the registration screen, a "forgot password" screen, an administrator "reset password for another user" screen, etc.
But in any of those cases, it's always going to be tied to some text input field. So that's where it should be coupled.
Make a very small React component that consists solely of an input field and the associated validation logic. Input that component within all of the forms that might want to have a password input.
It's essentially the same outcome as having a service/factory for the logic, but you're coupling it directly to the input. So you now never need to tell that function where to look for it's validation input, as it is permanently tied together.
Same situation: Having done multiple Angular projects and moving to React, not having a simple way to provide services through DI seems like a missing piece (the particulars of the service aside).
Using context and ES7 decorators we can come close:
https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
Seems these guys have taken it a step further / in a different direction:
http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs
Still feels like working against the grain. Will revisit this answer in 6 months time after undertaking a major React project.
EDIT: Back 6 months later with some more React experience. Consider the nature of the logic:
Is it tied (only) to UI? Move it into a component (accepted answer).
Is it tied (only) to state management? Move it into a thunk.
Tied to both? Move to separate file, consume in component through a selector and in thunks.
Some also reach for HOCs for reuse but for me the above covers almost all use cases. Also, consider scaling state management using ducks to keep concerns separate and state UI-centric.
I also came from Angular.js area and the services and factories in React.js are more simple.
You can use plain functions or classes, callback style and event Mobx like me :)
// Here we have Service class > dont forget that in JS class is Function
class HttpService {
constructor() {
this.data = "Hello data from HttpService";
this.getData = this.getData.bind(this);
}
getData() {
return this.data;
}
}
// Making Instance of class > it's object now
const http = new HttpService();
// Here is React Class extended By React
class ReactApp extends React.Component {
state = {
data: ""
};
componentDidMount() {
const data = http.getData();
this.setState({
data: data
});
}
render() {
return <div>{this.state.data}</div>;
}
}
ReactDOM.render(<ReactApp />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
</body>
</html>
Here is simple example :
I am from Angular as well and trying out React, as of now, one recommended(?) way seems to be using High-Order Components:
A higher-order component (HOC) is an advanced technique in React for
reusing component logic. HOCs are not part of the React API, per se.
They are a pattern that emerges from React’s compositional nature.
Let's say you have input and textarea and like to apply the same validation logic:
const Input = (props) => (
<input type="text"
style={props.style}
onChange={props.onChange} />
)
const TextArea = (props) => (
<textarea rows="3"
style={props.style}
onChange={props.onChange} >
</textarea>
)
Then write a HOC that does validate and style wrapped component:
function withValidator(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props)
this.validateAndStyle = this.validateAndStyle.bind(this)
this.state = {
style: {}
}
}
validateAndStyle(e) {
const value = e.target.value
const valid = value && value.length > 3 // shared logic here
const style = valid ? {} : { border: '2px solid red' }
console.log(value, valid)
this.setState({
style: style
})
}
render() {
return <WrappedComponent
onChange={this.validateAndStyle}
style={this.state.style}
{...this.props} />
}
}
}
Now those HOCs share the same validating behavior:
const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)
render((
<div>
<InputWithValidator />
<TextAreaWithValidator />
</div>
), document.getElementById('root'));
I created a simple demo.
Edit: Another demo is using props to pass an array of functions so that you can share logic composed by multiple validating functions across HOCs like:
<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />
Edit2: React 16.8+ provides a new feature, Hook, another nice way to share logic.
const Input = (props) => {
const inputValidation = useInputValidation()
return (
<input type="text"
{...inputValidation} />
)
}
function useInputValidation() {
const [value, setValue] = useState('')
const [style, setStyle] = useState({})
function handleChange(e) {
const value = e.target.value
setValue(value)
const valid = value && value.length > 3 // shared logic here
const style = valid ? {} : { border: '2px solid red' }
console.log(value, valid)
setStyle(style)
}
return {
value,
style,
onChange: handleChange
}
}
https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js
If you are still looking for a service like Angular, you can try the react-rxbuilder library
You can use #Injectable to register the service, and then you can use useService or CountService.ins to use the service in the component
import { RxService, Injectable, useService } from "react-rxbuilder";
#Injectable()
export class CountService {
static ins: CountService;
count = 0;
inc() {
this.count++;
}
}
export default function App() {
const [s] = useService(CountService);
return (
<div className="App">
<h1>{s.count}</h1>
<button onClick={s.inc}>inc</button>
</div>
);
}
// Finally use `RxService` in your root component
render(<RxService>{() => <App />}</RxService>, document.getElementById("root"));
Precautions
Depends on rxjs and typescript
Cannot use arrow functions in the service
Service is not limited to Angular, even in Angular2+,
Service is just collection of helper functions...
And there are many ways to create them and reuse them across the application...
1) They can be all separated function which are exported from a js file, similar as below:
export const firstFunction = () => {
return "firstFunction";
}
export const secondFunction = () => {
return "secondFunction";
}
//etc
2) We can also use factory method like, with collection of functions... with ES6 it can be a class rather than a function constructor:
class myService {
constructor() {
this._data = null;
}
setMyService(data) {
this._data = data;
}
getMyService() {
return this._data;
}
}
In this case you need make an instance with new key...
const myServiceInstance = new myService();
Also in this case, each instance has it's own life, so be careful if you want to share it across, in that case you should export only the instance you want...
3) If your function and utils not gonna be shared, you can even put them in React component, in this case, just as function in your react component...
class Greeting extends React.Component {
getName() {
return "Alireza Dezfoolian";
}
render() {
return <h1>Hello, {this.getName()}</h1>;
}
}
4) Another way you may handle things, could be using Redux, it's a temporary store for you, so if you have it in your React application, it can help you with many getter setter functions you use... It's like a big store that keep tracks of your states and can share it across your components, so can get rid of many pain for getter setter stuffs we use in the services...
It's always good to do a DRY code and not repeating what needs to be used to make the code reusable and readable, but don't try to follow Angular ways in React app, as mentioned in item 4, using Redux can reduce your need of services and you limit using them for some reuseable helper functions like item 1...
I am in the same boat like you. In the case you mention, I would implement the input validation UI component as a React component.
I agree the implementation of the validation logic itself should (must) not be coupled. Therefore I would put it into a separate JS module.
That is, for logic that should not be coupled use a JS module/class in separate file, and use require/import to de-couple the component from the "service".
This allows for dependency injection and unit testing of the two independently.
In the React world we have two types of logic: Stateful and stateless. Now this is the main concept to grasp when starting with React. That here we update state which should update UI as opposed to Angular's direct updates of dom. The two types of logics are:
That do not depend on state changes, i.e. static logic which doesn't need to re-render something based on state changes. For such cases just create regular js files and import them like a library or helper methods
If you have some code that depends on state and u need to resuse it then two options - hocs and the newer hooks. Hooks are a bit hard to wrap our heads around but basically they would force their parent to rerender if their internal state changes so any stateful logic can be defined and reused in different components, and each hook instance would have its own isolated scope.
It's a little bit of a thinking shift to understand state and declarative components but feel free to ask followup questions in comments
or you can inject the class inheritance "http" into React Component
via props object.
update :
ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
Simply edit React Component ReactApp like this:
class ReactApp extends React.Component {
state = {
data: ''
}
render(){
return (
<div>
{this.props.data.getData()}
</div>
)
}
}
It is possible to use export keyword to use functions from file which contains necessary methods.
Let me show an example. Let's say we have a file called someService.ts:
export const foo = (formId: string) => {
// ... the code is omitted for the brevity
}
export const bar = (): Entity[] => [
// ... the code is omitted for the brevity
]
export default {
foo,
bar,
}
Then we can use this service in component like this:
import {
foo,
bar,
} from './someService'
const InnerOrderModal: FC = observer(() => {
const handleFormClick = (value: unknown, item: any) => {
foo(item.key)
bar()
return <></>
}

Resources