In the React documentation they say:
React also supports using a string (instead of a callback) as a ref prop on any component, although this approach is mostly legacy at this point.
https://facebook.github.io/react/docs/more-about-refs.html
Take the following example:
class Foo extends Component {
render() {
return <input onClick={() => this.action()} ref={input => (this._input = input)} />;
}
action() {
console.log(this._input.value);
}
}
Why should I prefer this, instead of:
class Foo extends Component {
render() {
return <input onClick={() => this.action()} ref='input' />;
}
action() {
console.log(this.refs.input.value);
}
}
?
It seems just much more clean and easier the second example.
Are there risks that the string method will be deprecated?
NB: I'm looking for the "official" answer to the statement in the documentation, I'm not asking about personal preferences and so on.
While perhaps more simple, the old refs API can get difficult in some edge cases, like when used in a callback. All kind of static analysis is a pain with strings, too. The callback based API can do everything the string API can do and more with just a little added verbosity.
class Repeat extends React.Component {
render() {
return <ul> {
[...Array(+this.props.times)].map((_, i) => {
return <li key={i}> { this.props.template(i) } </li>
})
} </ul>
}
}
class Hello extends React.Component {
constructor() {
super();
this.refDict = {};
}
render() {
return <Repeat times="3" template={i => <span ref= {el => this.refDict[i] = el}> Hello {i} </span>} />
{/* ^^^ Try doing this with the string API */}
}
}
Further discussion and a bit more comprehensive list of the possible issues with the string based api can be found from issue #1373, where the callback based api was introduced. I'll include here a list from the issue description:
The ref API is broken is several aspects.
You have to refer to this.refs['myname'] as strings to be Closure Compiler Advanced Mode compatible.
It doesn't allow the notion of multiple owners of a single instance.
Magical dynamic strings potentially break optimizations in VMs.
It needs to be always consistent, because it's synchronously resolved. This means that asynchronous batching of rendering introduces potential bugs.
We currently have a hook to get sibling refs so that you can have one component refer to it's sibling as a context reference. This only works one level. This breaks the ability to wrap one of those in an encapsulation.
It can't be statically typed. You have to cast it at any use in languages like TypeScript.
There's no way to attach the ref to the correct "owner" in a callback invoked by a child. <Child renderer={index => <div ref="test">{index}</div>} /> -- this ref will be attached where the callback is issued, not in the current owner.
The docs call the old string API "legacy" to make it clearer that the callback-based API is the preferred approach, as is discussed in this commit and in this PR which are the ones that actually put those statements to the documentation in the first place. Also note that a few of the comments imply that the string based refs api might be deprecated at some point.
Originally posted by danabramov on https://news.ycombinator.com/edit?id=12093234
String refs are not composable. A wrapping component can’t “snoop” on a ref to a child if it already has an existing string ref. On the other hand, callback refs don’t have a single owner, so you can always compose them.
String refs don’t work with static analysis like Flow. Flow can’t guess the magic that framework does to make the string ref “appear” on this.refs, as well as its type (which could be different). Callback refs are friendlier to static analysis.
The owner for a string ref is determined by the currently executing component. This means that with a common “render callback” pattern (e.g. <DataTable renderRow={this.renderRow} />), the wrong component will own the ref (it will end up on DataTable instead of your component defining renderRow).
String refs force React to keep track of currently executing component. This is problematic because it makes react module stateful, and thus causes weird errors when react module is duplicated in the bundle.
Related
I recently switched from React to Vue. In React, i often had a method in a child component, that would look something like this:
onClick = (e)=>{
const val = e.target.value;
this.props.onClick(val)
}
I see that Vue is very "free", and allows you to treat component methods and props as "the same thing".
Is there some way to distinguish between the two, like with this.props? What is the convention regarding this issue?
Props, data, computed properties and methods end up as properties on Vue instance - members may also include lifecycle hooks when a component is declared as a class. As with any other class, their names may collide.
In case there are members that are private (onClick method) and are parts of public API (onClick prop), they can be named differently, e.g. with underscore naming convention that is common in JS OOP:
...
props: {
onClick: Function
},
methods: {
_onClick() {...}
}
...
The use of naming conventions for private properties is suggested by Vue style guide.
In composition API and Vue 3, there is a clear distinction in setup because props object is available, so it could be done similarly to how the question suggests. There is no difference between props and instance properties in templates so the rest of the answer is still applicable.
You should take advantage of another Vue technique, instead of passing a function, using its event emitter system:
// MyComponent:
methods: {
onClick(e) {
const val = e.target.value;
this.emit('click', val);
}
}
...
<button #click="onClick">Click me</button>
//When you actually use your component
<MyComponent #click="() => { console.log('delegated onClick!'); }"/>
As mentioned in one of the comments try to avoid using duplicate names for props, methods, etc...
I have a large React app and I have a few components that I would like to completely disable from a config or global level. Is there any kind of global hook that I can use that is called before any component is rendered? If so, I imagine I can check the name of the component and return null if the name is on the disabled list. How would you do this?
There are a lot of ways to do this:
React's Context API allows you pass props through every level of the component tree so you can use them as flags to enable/disable components. Should be used sparingly however.
Higher Order Components are basically just functions that return a component. You could wrap your components in logic to render them as needed.
Or of course you could use a global state manager like redux to set global states.
There are many ways to do this, so, I'll just describe one simple way: using references and updating the states accordingly.
Full working feature hide/showing sandbox online: codesandbox.io ReactJS Feature Hide/Show Demo
Defined are two classes, class Feature extends React.Component and class App extends React.Component. The render() for <Feature/> is...
render() {
if (!this.state.enabled) {
return <div />;
}
return (
<div className="Feature">
<h1>My Feature!</h1>
</div>
);
}
And the option for enabling/disabling a feature in <App /> would handle display/hiding like so...
handleOnClick(e) {
if (e.target.checked) {
this.feature.setState({ enabled: true });
} else {
this.feature.setState({ enabled: false });
}
}
Of course, you need to make sure that <Feature /> has the reference set...
<Feature
ref={instance => {
this.feature = instance;
}}
/>
If you need simplest solution just use browser global vars and check it in render.
render() {
if( window.globalFlag ) return null
return (
<div> feature content...
Drawbacks:
modifying component,
using global scope,
some unnecessary code can be run earlier (f.e. constructor) and later (f.e. componentDidMount).
Use HOCs - wrap your component - connecting with global store using redux or context API.
<FlagsProvider store={flagStore}>
<SomeComponent_1>
<SomeComponent_2>
<FlagsConsumer flag="someFeatureFlag">
<SomeFeatureComponent />
<FlagsConsumer/> connects to store (redux connect would be an inner wrapper - composing HOCs) and conditionally renders <SomeFeatureComponent /> (or null).
Of course HOC can pass received props to wrapped component - it can be functionally transparent.
Don't reinvent the wheel - use some ready module, read tutorials, google for sth suitable.
HOC can also play a role of A/B testing.
I have a legacy Backbone app which I have begun to rewrite in React. The app has a main view containing two subviews, arranged vetically. The top panel displays some data, and the bottom one displays the result of some algorithm taking this data as input. Since I have many different data sources, each with a different algorithm applied to it, I have an abstract base View class, which I then subclass for each data source, adding, decorating and overriding methods as necessary. Somewhat like this:
// Base View.
const BaseView = Backbone.View.extend({
events: {},
initialize() {
this.subViewA = // instantiate subview...
this.subViewB = // instantiate subview...
},
generateResultData() {
// 'Abstract' method which should be specialised to generate data rendered by subViewB...
},
render() {
// render subviews...
},
});
// Derived View.
const Derived = BaseView.extend({
events: {
// event handlers...
},
add(a, b) {
return a+b;
},
// additional methods...
generateResultData() {
return {
result: this.add(2,2);
}
},
})
This results in a shallow hierarchy of many similar View classes. It's all terribly imperative, but it's a simple, intuitive and easy-to-reason-about pattern, and just works. I'm struggling to see how to achieve the same thing in React, however. Given that subclassing of subclasses of React.Component is considered an anti-pattern, my focus has naturally been on composition, and in particular Higher Order Components. HOCs (which I find beautiful, but unintuitive and often just downright confusing) seem to involve adding general features, rather than specialising/refining something more general. I have also considered passing in more specialised versions of Componenet methods through props. but that just means I have to use the same boilerplate Component definition over and over again:
// General functional component, renders the result of prop function 'foo'.
function GeneralComponent(props) {
const foo = this.props.foo || ()=>"foo";
return (
<div>
<span> { this.props.foo() } </span>
</div>
)
}
// Specialised component 1, overrides 'foo'.
class MySpecialisedComponent extends React.Component {
foo() {
return this.bar()
}
bar() {
return "bar"
}
render() {
return (
<GeneralComponent foo={this.foo} />
)
}
}
// Specialised component 2, overrides 'foo' and adds another method.
class MyOtherSpecialisedComponent extends React.Component {
foo() {
return this.bar() + this.bar()
}
bar() {
return "bar"
}
baz() {
return "baz"
}
render() {
return (
<GeneralComponent foo={this.foo} />
)
}
}
The above is a very simplistic case, obviously, but essentially captures what I need to do (though I would of course be manipulating state, which the example does not do, for simplicity). I mean, I could just do things like that. But I want to avoid having to repeat that boilerplate all over the place. So is there a simpler and more elegant way of doing this?
Generally, if a component is stateless and doesn't use lifecycle hooks, there are no reasons for it to be Component class. A class that acts as a namespace and doesn't hold state can be considered an antipattern in JavaScript.
In constrast to some other frameworks, React doesn't have templates that would need to map variables in order for them to be available in view, so the only place where bar function needs to be mentioned is the place where it's called. JSX is an extension over JavaScript, JSX expressions can use any names that are available in current scope. This allows to compose functions without any classes:
const getBar => "bar";
const getBaz => "baz";
const getBarBaz => getBar() + getBaz();
const MySpecialisedComponent = props => <GeneralComponent foo={getBar} />;
const MyOtherSpecialisedComponent = props => <GeneralComponent foo={getBarBaz} />;
An anonymous function could be passed as foo prop instead of creating getBarBaz but this is generally discouraged because of unnecessary overhead.
Also, default prop values could be assigned with defaultProps without creating new ()=>"foo" function on each component call:
function GeneralComponent({ foo }) {
return (
<div>
<span> {foo()} </span>
</div>
)
}
GeneralComponent.defaultProps = { foo: () => 'foo' };
IMO what is throwing you off isn't inheritance vs composition, it's your data flow:
For example, many of my derived views need to do custom rendering after the main render. I'm using a third-party SVG library, and the data rendered into the 'result' subview is derived from analysis of rendered SVG elements in the main data view above it
So what you're trying to do here is have a child update props of a distantly related component after render, correct? Like this?
// after the svg renders, parse it to get data
<div id="svg-container">
<svg data="foo" />
<svg data="bar />
</div>
// show parsed data from svg after you put it through your algos
<div id="result-container">
// data...
</div>
There's a lot of state management libraries out there that will help you with this problem, that is, generating data in one component and broadcasting it to a distantly related component. If you want to use a tool built-in to react to address this you may want to use context, which gives you a global store that you can provide to any component that wants to consume it.
In your example your child classes have data-specific methods (add, etc.). IMO it's more typical in react to have a generic class for displaying data and simply passing it down map functions as props in order to rearrange/transform the rendered data.
class AbstractDataMap extends PureComponent {
static defaultProps = {
data: [],
map: (obj, i) => (<div key={i}>{obj}</div>)
};
render() {
const { data, map, children } = this.props;
const mapped = data.map(map);
return (
<Fragment>
{mapped.map((obj, i) => (
children(obj, i)
))}
</Fragment>
);
}
}
// in some other container
class View extends Component {
render() {
return (
<div>
<AbstractDataMap data={[1, 2, 3]} map={(n) => ({ a: n, b: n + 1 })}>
{({ a, b }, i) => (<div key={i}>a: {a}, b: {b}</div>)}
</AbstractDataMap>
<AbstractDataMap data={[2, 4, 6]} map={(n) => (Math.pow(n, 2))}>
{(squared, i) => (<div key={i}>squared: {squared}</div>)}
</AbstractDataMap>
</div>
);
}
}
IMO this pattern of using an HOC to abstract away the labor of explicitly using .map in your render calls (among other uses) is the pattern you are looking for. However, as I stated above, the HOC pattern has nothing to do your main issue of shared data store across sibling components.
Answering my own question, which I've never donw before...
So my question really arose from a concern that I would need to refactor a large, imperative and stateful codebase so as to integrate with React’s composition-based model (also with Redux). But it occurred to me after reading the (very insightful and helpful) responses to my question that my app has two parallel parts: the UI, and an engine which runs the algorithms (actually it's a music analysis engine). And I can strip out the Backbone View layer to which the engine is connected quite easily. So, using React’s context API I've built an ‘AnalysisEngineProvider', which makes the engine available to subcomponents. The engine is all very imperative and classically object-oriented, and still uses Backbone models, but that makes no difference to the UI as the latter has no knowledge of its internals - which is how it should be (the models will likely be refactored out at some point too)...
The engine also has responsibility for rendering the SVG (not with BB views). But React doesn’t know anything about that. It just sees an empty div. I take a ref from the div and pass it to the engine so the latter knows where to render. Beyond that the engine and the UI have little contact - the divs are never updated from React state changes at all (other components of the UI are though, obviously). The models in the engine only ever trigger updates to the SVG, which React knows nothing about.
I am satisfied with this approach, at least for now - even if it's only part of an incremental refactor towards a fully React solution. It feels like the right design for the app whatever framework I happened to be using.
I recently watched a talk by David Nolen where he says that 'immutability is an implementation detail in React'?
What does this mean and if this wasn't the case, how would React be different?
What does "implementation detail" mean:
I would summarize as:
Immutability is a detail of react that you have to implement yourself.
BTW: "Detail" is this case can still mean a lot of work.
React depends on props and state to be immutable.
React does not make props or state immutable for you. You have to ensure that in your code yourself.
So the following code is a recipe for disaster:
// DO NOT TRY THIS AT HOME
var customerObject = { name: "Bill" };
this.setState( customer: customerObject }; // valid react code, triggering re-render
...
customerObject.name = "Karl";
// state still has the same customerObject,
// but the contents of the object have changed. This is where things break down.
React has to ensure that its internal virtual DOM, and all props and states, are always in sync with the actual DOM.
So every time something changes anywhere in a prop or state, react needs to run its render cycle.
How would react be different without immutability:
Without immutability your react implementation may not work properly.
If react were not designed for immutability, then it would not be react (i.e. a state machine) but a different beast altogether.
Immutable Data Structure with ReactJS
The first of all, react team strongly recommend applying immutable data structure like Immutability Helpers or immutable.js. Why? Because we can use "shallow comparison" to increase component re-render performance. like
MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
}
render() {
...
}
}
According to immutability, the data alway return a new reference if it has been changed. We can easy use shallowEqual(only check reference whether is same or not) to determine component will re-render. If we dont use immutable data, we have to check props or state object deeply not reference to make sure re-rendering.
As for my understanding, each component in React has its own standalone scope and they don't share the variables.
That means when you pass an mutable variable(such as Object or Array) through props to a specific react component. It will clone each variable so that this component will have a totally new environment.
For example, assuming you have component A, and it works like this,
var ComponentA = React.createClass({
render: function() {
var user = { name: 'Tyler', role: 'Developer' };
return (
<SubComponent user={user} />
);
}
});
What ComponentA wants is simply render the user. So it require another module, let's say SubComponent to do that.
var SubComponent = React.createClass({
render: function() {
return (
<div>
<span>Name: {this.props.user.name}</span>
<span>Role: {this.props.user.role}</span>
</div>
);
}
});
For now, we should notice the variable user in ComponentA is different with the variable this.props.user in SubComponent. The this.props.user is not a reference. It's cloned from the ComponentA.
So that means, when you try to change the value of this.props.user in SubComponent, it won't destroy the user in ComponentA. Which is what David Nolen said in his tech talk. ("Change something in data without destroy the old one.")
Of course this would sacrifice some extra spaces, but you can get lots of benefits. Such as each of your component would be totally separated. Then all the nightmares cause by Shared Mutable Variables are gone. Shared Mutable Data is the root of evil, it's unpredictable and unreliable.
Imagine the SubComponent and the ComponentA are share the same user and you want to render another module by passing props user. Then you will update your code into this way,
var ComponentA = React.createClass({
render: function() {
var user = { name: 'Tyler', role: 'Developer' };
return (
<div>
<AnotherComponent user={user} />
<SubComponent user={user} />
</div>
);
}
});
Once we change the name of user in SubComponent(maybe by accident), we will have a cascading effect, and we don't know which one change the variable. That's painful coz then we have to check each line of the code in SubComponent and AnotherComponent. You really don't want to do that, right?
So I think that's what he mean. Hope this can solve your problem. : )
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 <></>
}