Best way to sharing function in react - reactjs

I'm new in react. I wonder what is the best option for sharing functions (except inheritance). In inheritance way I can do something like this:
class BaseForm {
updateForm() => {};
}
class MyFormOne extends BaseForm {
// this.updateForm();
}
And I can code few Form components which extends BaseForm and I can use updateForm method in every. What is the best way in react to do this?

There are multiple ways for the question :
1) If the shared fnc is the static function (independent with the component). You should create a common file utils.js and then export, import function wherever you want
// utils.js
export function updateForm() { }
// MyformOne.js
import {updateForm} from './utils';
// Use fnc updateForm() in MyFormOne component
2) If the shared fnc is dependent with class component, you should use the design pattern in React : HOC (Higher Order Component) or render Props for sharing code (states, functions) between React components (render your code more abstract => a good practice for reusable)
class BaseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
a1 : "",
a2 : "",
}
}
// example : share the func updateForm of Baseform
updateForm = () => {}
render() {
return (
<div>
{/*
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state, this.updateForm)}
</div>
);
}
}
class WithBaseForm extends React.Component {
render() {
return (
<div>
<BaseForm render={(stateBaseForm, updateForm) => (
<MyFormOne stateBaseForm={stateBaseForm} updateForm={updateForm}/> // so MyFormOne could use the fnc updateForm of BaseForm
)}/>
</div>
);
}
}
You should view the link for more details :
https://en.reactjs.org/docs/render-props.html
https://en.reactjs.org/docs/higher-order-components.html

You can create a utils.js file and place your common JS functions there. Something like this:
export const updateFormCommon = (inputValue) => {
// Your function logic here
return something;
};
export const showMessage = () => {
alert('hello world');
};
Now you can import utils.js wherever you need and call your common\shared functions like this:
import {updateFormCommon, showMessage} from './utils'
let result = updateFormCommon(1000)}
showMessage();

Related

ReactJS Question Component function scope and sharing functions

I have a question about ReactJS and components, specifically about how functions interact within the component system.
In this example:
// Index.js
import React from ‘/reactʼ;
import ReactDOM from ‘/react-domʼ;
import App from ‘./App.jsʼ;
ReactDOM.render(<App />, document.getElementById(‘rootʼ));
// App.js
import React from ‘/reactʼ;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {someProp = ‘ʼ};
};
functionA = (e) => { console.log(e);
};
Render() {
return <div><ComponentA /></div>
};
};
export default App;
// ComponentA.js
import React from ‘/reactʼ;
import App from ‘./../App.jsʼ;
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.state = {someProp = ‘ʼ};
};
functionB = App.functionA
Render() {
return(
<div>
<input onSubmit={this.functionB} />
</div>
);
};
};
export default ComponentA;
ComponentA imports App.js and attempts to assign App.functionA to functionB and then call it in the JSX. This results in a failure basically saying that the function is not defined.
I know this is NOT the way to function share (I have learned about passing functions through props etc).
I simply just want to know WHY this does not work, to help me better understand the mechanics of React, and Javascript in general.
Thank you,
Curtis
To call a function from another React component, you can write static methods in ES6 notation. If you are using ES7, then you can also write static properties.
You can write statics inside ES6+ classes this way:
class Component extends React.Component {
static propTypes = {
...
}
static someMethod(){
}
}
Working Demo about static function
My noob brain finally figured it out lol... I think.
Basically because an instance of the class [the App component] was not initialized within the scope of ComponentA, the App function is not accessible.
This made it work (DISCLAIMER: I DO NOT PLAN ON DOING THIS, I KNOW ITS TERRIBLE CODE)
// ComponentA.js
import React from ‘/reactʼ;
import App from ‘./../App.jsʼ;
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.state = {someProp = ‘ʼ};
this.appInstance = new App();
}
functionB = (e) => {
this.appInstance.functionA(e);
}
Render() {
return(
<div>
<input onSubmit={this.functionB} />
</div>
);
}
};
export default ComponentA;

Pass dynamic value to HOC in react

I write some HOC and I need to pass to this HOC a dynamic object that I create on some life cycle level and I did not get him as a prop.
If I try to pass some static value ( for example initialize myObj from start) it works as expected and I get the correct value.
Let's say this is my component class :
let myObj = {};
class Test extends React.Component
{
constructor(props) {
super(props);
.....
}
render() {
myObj = {test:'test'};
return ( ... )
}
}
export default withHOC(Test, myObj);
And this is my HOC:
const withHOC = (Component, test) => {
class Hoc extends React.Component
{
constructor(props)
{
super(props);
const s = test; // ---->test is empty object always !!
...
}
}
return Hoc;
}
My 'Dynamic' object that I create on my 'test' class is always empty on my HOC class.
It's happend also when I try to pass some value from my props directly, in this case the page is stuck(without errors in console).
Does someone have any idea how to resolve that? Thanks!
When you compose a component that way, composition only happens at compile time (static composition). This means that withHOC runs only once and is receiving an empty myObj argument, as it is using the one defined on declaration.
export default withHOC(Test, myObj); //myObj = {}
If you want that value to be dynamic, the withHOC composition should be runned when that value changes.
You can't send data up from the WrappedComponent (Test) to the HOC (withHOC), so even if you change myObj value in Test.render, the HOC would never know.
What you could do, if you really need it, is do the composition on the Test.render
render(){
const Hoc = withHOC(this.state.myObj, WrappedComponent);//WrappedComponent can be anything
return(
<Hoc/>
)
}
This way, every time the component renders, Hoc is composed using as myObj a value from the component state, wich is not the preferable way to do it, because this.state.myObj might have the same value as it did at the previous render, and you would be re-composing with the same data as before.
A better way to do it is checking for changes in myObj at Test.componentDidUpdate, and if it did change, then compose Hoc again.
You are passing an empty object to the withHOC function
let myObj = {}; // <- your myObj is empty
class Test extends React.Component
{
constructor(props) {
super(props);
.....
}
render() {
myObj = {test:'test'}; // <- You're doing this in the render method of your Test component, so until the component is rendered myObj is empty
return ( ... )
}
}
export default withHOC(Test, myObj);
Some explanation about what's happening here, by order:
import Comp from '.../Test.js'
the withHOC function is triggered, with the params of Test (which is defined above the call) and myObj (which is defined above the call but is empty)
Test component is returned, and nobody used the logic of myObj = {test:'test'}
Suggested solution:
Make the HOC get the logic from the props with another hoc:
const withProps = newProps => BaseComponent => props => {
const propsToAdd = typeof newProps === 'function' ? newProps(props) : newProps
return <BaseComponent {...props} {...propsToAdd} />
}
Usage:
class Test extends React.Component
{
constructor(props) {
super(props);
.....
}
render() {
return ( ... )
}
}
export default withProps({test:'test'})(withHOC(Test));
// or: export default withProps(props => {test:'test'})(withHOC(Test));
const withHOC = (Component) => {
class Hoc extends React.Component
{
constructor(props)
{
super(props);
const s = this.props.test;
...
}
}
return Hoc;
}
you can use recompose, a library which has many hocs and utils, and for better readability:
import { compose, withProps } from "recompose"
class Test extends React.Component {...}
const enhance = compose(
withProps({test:'test'}),
withHOC
)
export default enhance(Test);
I can't say with confidence this is optimal but I solved a similar problem by having a function within the HOC that updates state that you can then invoke with any data in the wrapped component.
HOC:
func = (a, b) => {
this.setState({
stateA: a,
stateB: b
)}
}
return ({ <WrappedComponent func={this.func} /> })
Wrapped Component:
this.props.func(anythingA, anythingB);
You can then access the data through state in the HOC.
To elaborate:
const withHOC = (WrappedComponent) => {
class withHOC extends React.Component {
constructor(props) {
super(props)
this.state = {
stateA: 1,
stateB: 2
}
*use state however you want in this HOC, including pass it through to another component*
*the following is just a function*
*when it's invoked in the wrapped component state will update here in the
HOC*
changeState = (a, b) => {
this.setState({
stateA: a,
stateB: b
)}
}
render() {
return (
<div>
<p>this.state.stateA</p>
<p>this.state.stateB</p>
<WrappedComponent changeState={this.changeState} />
</div>
)
}
}
}
}
In wrappedComponent, after importing:
class aComponent extends Component {
constructor(props) {
super(props)
this.state = {
}
*you can now invoke the function from the HOC in this wrapped component*
}
}
You can use react-redux and store your object in redux state. Change the object wherever you need (in your case it's in Test) and access it in component inside your HOC from redux state, it'll be always up to date.

How to get the data from React Context Consumer outside the render

I am using the new React Context API and I need to get the Consumer data from the Context.Consumer variable and not using it inside the render method. Is there anyway that I can achieve this?
For examplify what I want:
console.log(Context.Consumer.value);
What I tested so far: the above example, tested Context.Consumer currentValue and other variables that Context Consumer has, tried to execute Context.Consumer() as a function and none worked.
Any ideas?
Update
As of React v16.6.0, you can use the context API like:
class App extends React.Component {
componentDidMount() {
console.log(this.context);
}
render() {
// render part here
// use context with this.context
}
}
App.contextType = CustomContext
However, the component can only access a single context. In order to use multiple context values, use the render prop pattern. More about Class.contextType.
If you are using the experimental public class fields syntax, you can use a static class field to initialize your contextType:
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* render something based on the value */
}
}
Render Prop Pattern
When what I understand from the question, to use context inside your component but outside of the render, create a HOC to wrap the component:
const WithContext = (Component) => {
return (props) => (
<CustomContext.Consumer>
{value => <Component {...props} value={value} />}
</CustomContext.Consumer>
)
}
and then use it:
class App extends React.Component {
componentDidMount() {
console.log(this.props.value);
}
render() {
// render part here
}
}
export default WithContext(App);
You can achieve this in functional components by with useContext Hook.
You just need to import the Context from the file you initialised it in. In this case, DBContext.
const contextValue = useContext(DBContext);
You can via an unsupported getter:
YourContext._currentValue
Note that it only works during render, not in an async function or other lifecycle events.
This is how it can be achieved.
class BasElement extends React.Component {
componentDidMount() {
console.log(this.props.context);
}
render() {
return null;
}
}
const Element = () => (
<Context.Consumer>
{context =>
<BaseMapElement context={context} />
}
</Context.Consumer>
)
For the #wertzguy solution to work, you need to be sure that your store is defined like this:
// store.js
import React from 'react';
let user = {};
const UserContext = React.createContext({
user,
setUser: () => null
});
export { UserContext };
Then you can do
import { UserContext } from 'store';
console.log(UserContext._currentValue.user);

ReactJS - How to use method from other component of other file? [duplicate]

I have two components. I want to call a method of the first component from the second component. How can I do it?
Here is my code.
First Component
class Header extends React.Component{
constructor(){
super();
}
checkClick(e, notyId){
alert(notyId);
}
}
export default Header;
Second Component
class PopupOver extends React.Component{
constructor(){
super();
// here i need to call Header class function check click....
// How to call Header.checkClick() from this class
}
render(){
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default PopupOver;
You can do something like this
import React from 'react';
class Header extends React.Component {
constructor() {
super();
}
checkClick(e, notyId) {
alert(notyId);
}
render() {
return (
<PopupOver func ={this.checkClick } />
)
}
};
class PopupOver extends React.Component {
constructor(props) {
super(props);
this.props.func(this, 1234);
}
render() {
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default Header;
Using statics
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
render: function() {
}
});
MyComponent.customMethod('bar'); // true
Well, actually, React is not suitable for calling child methods from the parent. Some frameworks, like Cycle.js, allow easily access data both from parent and child, and react to it.
Also, there is a good chance you don't really need it. Consider calling it into existing component, it is much more independent solution. But sometimes you still need it, and then you have few choices:
Pass method down, if it is a child (the easiest one, and it is one of the passed properties)
add events library; in React ecosystem Flux approach is the most known, with Redux library. You separate all events into separated state and actions, and dispatch them from components
if you need to use function from the child in a parent component, you can wrap in a third component, and clone parent with augmented props.
UPD: if you need to share some functionality which doesn't involve any state (like static functions in OOP), then there is no need to contain it inside components. Just declare it separately and invoke when need:
let counter = 0;
function handleInstantiate() {
counter++;
}
constructor(props) {
super(props);
handleInstantiate();
}
You could do this to call a method of the child component from the parent component.
import React from 'react';
class Header extends React.Component {
constructor() {
super();
this.childComponentRef;
}
getChildComponent = (childComponent) => {
this.childComponentRef = childComponent;
this.childComponentRef.sayHi();
}
render() {
return (
<ChildComponent getChildComponent={this.getChildComponent} />
)
}
};
class ChildComponent extends React.Component {
componentDidMount () {
this.props.getChildComponent(this);
}
sayHi = () => {
alert("hi");
}
render() {
return (
<div className="displayinline col-md-12 ">
Hello
</div>
);
}
}
export default Header;

React cloneElement and component instance

I have the following higher order component that I am trying to wrap in a container element that is supplied as a prop:
import React, { PropTypes } from 'react';
export default (Component) => {
return class extends React.Component {
static propTypes = {
containerElement: PropTypes.element
}
static defaultProps = {
containerElement: <div />
};
componentDidMount() {
console.log(this.el);
}
render() {
const containerProps = {
ref: (el) => this.el = el
};
return React.cloneElement(containerElement, containerProps, Component);
};
}
}
I then wrap a component like this:
export default AnimationComponent(reduxForm({
form: 'newResultForm',
validate
})(NewResultForm));
But when I log the element in componentDidMount it is an empty <div/>.
Why is the passed in component not a child of the newly created container element?
Your method of writing a Higher Order Component is a little unorthodox. React developers typically don't have to write functions that accept components and return a new class definition unless they're writing something like redux-form itself. Perhaps instead of passing Component as an argument, see if passing it in props.children will work for you:
<AnimationComponent>{NewResultForm}</AnimationComponent>
I'd define AnimationComponent like the following:
export default class AnimationComponent extends React.Component {
static propTypes = {
containerElement: React.PropTypes.element
};
static defaultProps = {
containerElement: <div />
};
render () {
// For each child of this component,
// assign each a ref and store it on this component as this[`child${index}`]
// e.g. this.child1, this.child2, ...
// Then, wrap each child in the container passed in on props:
return React.Children.map(this.props.children, (child, index) =>
React.cloneElement(
this.props.containerElement,
{ref: ref => this[`child${index}`] = ref},
React.cloneElement(child)
)
);
}
}
Instead of wrapping the form component in AnimationComponent, just export the connected form class:
export default reduxForm({
form: 'newResultForm',
validate
})(NewResultForm));
Now instead of being stuck with how AnimationComponent was configured in NewResultForm's file, we can configure it to our liking where we end up rendering the form. In addition to providing flexibility, the information needed to configure AnimationComponent will be more pertinent where it gets rendered:
export default class MyApp extends React.Component {
render() {
return (
<AnimationComponent containerComponent="span">
<NewResultForm />
</AnimationComponent>
);
}
}
I hope this helped!

Resources