ReactJS: componentWillUnmount not working across project - reactjs

I have an issue with ReactJS. Specifically componentWillUnmount is not being called across the project at all (a semi-large corporate website). I will list the steps I have taken to diagnose and pinpoint the issue:
I am using a simple console.log message to test, to exclude problems inside the function
I have placed it in different jsx files in the project, to exclude function positioning problems
I have checked against switching pages in the app and loading a different website altogether
I checked to see whether the function is called from parents, siblings or children, since I've found competing lifecycle calls can cause neither to work, but it's the only one in the project so far
I tried it on a different branch with no effect
I tried it on a colleague's computer with no effect
A different lifecycle function (componentWillMount) works fine
I am using ES6 with react 15.1.0, react-bootstrap and react-validation. The relevant code is below, but keep in mind I have placed the function in numerous places and it does not appear to get called anywhere.
export default class YourData extends React.Component {
constructor(props){
super(props);
this.getMainBookers = this.getMainBookers.bind(this);
this.bookingTravelCompanyForms = this.props.your_data.bookings.map(
(booking, i) => {
var mainBookers = i > 0 ? this.getMainBookers : false;
return (
<BookingTravelCompanyForm booking={booking} key={i}
mainBookers={mainBookers}
onInputChange={this.props.onInputChange}/>
);
}
);
}
componentWillMount(){
this.props.initializeInput();
}
componentWillUnmount(){
console.log('willunmount yourdata');
this.props.saveInput();
}
getMainBookers() {
var mainform = this.bookingTravelCompanyForms[0];
return mainform.props.booking.company;
}
handleSubmit(event) {
event.preventDefault();
// Emulate async API call
setTimeout(() => {
this.refs.form.showError('username', <span onClick={this.removeApiError.bind(this)}
className='form-error is-visible'>API Error. Click to hide out.</span>);
}, 1000);
}
copyTravelCompany() {
var travelCompany = this.bookingTravelCompanyForms[0];
this.setState({
travelCompany: travelCompany
});
}
render() {

Related

how to apply Full screen striction in react js?

Here i am developing an examination system. I want to apply full screen when i launch exam component.
While doing student strict to remain in full screen. When he/she exit from full screen then automatically call a function to save exam result.
There are some limitations when it comes to displaying things fullscreen but I suggest you to use https://github.com/sindresorhus/screenfull.js . It's quite well maintained and makes it easier for you to support multiple browsers.
In React you could do something like this:
// import/exports depend on your setup, this is just an example
const screenfull = require('screenfull');
class MyComponent extends React.Component {
componentDidMount() {
if (screenfull.isEnabled) {
screenfull.on('change', () => {
console.log('Am I fullscreen?', screenfull.isFullscreen ? 'Yes' : 'No');
});
}
}
// enabling fullscreen has to be done after some user input
toggleFullScreen = () => {
if (screenfull.isEnabled) {
screenfull.toggle();
}
}
render() {
return (
<button onClick={this.toggleFullScreen}>Toggle fullscreen</button>
)
}
}
LE:
To detect a fullscreen change you can use the screenfull.on('change') event: https://github.com/sindresorhus/screenfull.js#detect-fullscreen-change
Added code for this in the above example.

Is there any way to use ComponentDidMount() and ComponentDidUpdate() in Application Customiser Extension in SPFX

I'm trying to implement a webpart code inside a Extension , I was able to do that . But When i run the code ComponentDidMount() ComponentDidUpdate() are not getting called. Is it possible to implement that or it's not possible? I want to know that.
Feel free to share your knowledge
Thanxxx
From a practical point of view, I don't think you want to run a componentDidUpdate() in an extension unless you have a pretty good if statement.
But I think a componentDidMount should work.
In my customizer.ts file, I create the React Element after the Init
const element: React.ReactElement<IAcgBobBotProps> = React.createElement(
AcgBobBot,
{
webChatToken: this.properties.webChatToken
}
);
ReactDom.render(element, this._bottomPlaceholder.domElement);
And I create a component in a new \components\AcgBobBot.tsx TSX file:
export default class AcgBobBot extends React.Component<IAcgBobBotProps, IAcgBobBotState> {
public componentDidMount() {
console.log("Hi from componentDidMount");
}
public render(): React.ReactElement<IAcgBobBotProps> {
...
}
I, personally, also put my React "web part" into a placeholder, exactly copying the code from here: https://learn.microsoft.com/en-us/sharepoint/dev/spfx/extensions/get-started/using-page-placeholder-with-extensions

Migrating away from componentWillReceiveProps

The componentWillReceiveProps is becoming deprecated, however, I am unclear as to how to migrate away from it. For example, a simplified version of my current looks something like this:
import Reorder, {reorder, reorderImmutale, reorderFromTo, reorderFromToImmutable} from 'react-reorder'
class ObjectsArea extends React.Component {
constructor(props) {
super(props);
this.state = {
items: this.props.objects ? this.props.objects.items : []
};
}
componentWillReceiveProps(nextProps){
//May have to do a deep compare between nextProps.items and current items?
if (nextProps.objects){
this.setState({items: this.nextProps.objects.items})
}
}
onReorder (event, previousIndex, nextIndex, fromId, toId) {
let new_items = reorder(this.state.items, previousIndex, nextIndex)
this.setState({
items: new_items
});
//call to parent function
}
render(){
orderable_items = <Reorder reorderId="objects" onReorder={this.onReorder.bind(this)}>
{
this.state.items.map(item => (
<div key={item.id}>
{item.text}
</div>
))
}
</Reorder>
return (
<div>{orderable_items}</div>
)
}
My requirements:
Sometimes there will be no objects property (there isn't one on initial load)
When there is an objects property a sortable/draggable list is created using the react-reorder component
When items in the list are dragged to be rearranged the onReorder function is called.
The onReorder function should do two things: update the list on the screen, call a parent function passed in from props.
Currently all of this will work with componentWillReceiveProps, however, what is the proper way to migrate away from componentWillReceiveProps based on the above requirements?
While Tolsee's answer is perfectly correct it is also worth mentioning that the react docs suggest removing derived state (state that is calculated based on props) altogether. There is a great article here that is a great read in my opinion.
Your example fits the Anti-pattern: Unconditionally copying props to state example perfectly.
Without knowing your environment I cannot recommend a solution certainly, but to me it looks like you will be able to use the Fully controlled component example.
In that case, you'd need to lift your state up, simply use objects.items to render your Reorder child, and during the onReorder event simply call a function that you received as a prop.
In your problem you can do.
static getDerivedStateFromProps(nextProps, prevState){
if (nextProps.objects){){
return {items: this.nextProps.objects.items};
}
else return null;
}
Please follow this post for better understanding

Design For Loading Components

I am struggling with the design of a React application which I am building. The app has a chat window on the left and the content of the application on the right. When a user enters a command, the backend understands the nature of the command (uses Luis.ai and Microsoft Bot Framework) which all works as expected. However the React piece is where I'm struggling.
Say the user enters a command which states they want to update a person by entering "Update name to Bill". The application correctly understands that the command is update the person and should load the edit version of my person overview.
However I'm not sure exactly the best approach on how to do this. The approach I'm taking now basically loads an OverviewWrapper component. Inside of the Overview wrapper component based on the props passed to it should load either the Edit or the View pane. The View pane is loaded by default.
I guess I'm not sure if I should be trying to load the edit component by changing the state or if I should be trying to use the navigate function. Thank you in advance for your guidance.
Here is my code.
export default class Patient extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.directLine = new DirectLine({
secret: "SOMEVALUEHERE"
});
this.state = {
PATIENT: [],
COMPPROPS: [],
};
this.setPanelState = this.setPanelState.bind(this);
}
//Set State of COMPPROPS so that compState should be edit
setPanelState(activity) {
var _compState = 'Edit';
var _compName = 'Overview';
this.setState({COMPPROPS: [{compName: 'Overview', compState: _compState}]});
return _compState;
}
componentWillMount() {
getPatient().then((result) => {
this.setState({PATIENT: result});
});
//review any chat activity and if the command is update patient then run setpanelstate method which should set the state
this.directLine.activity$
.filter(function (activity) {
return activity.type === 'event' && activity.value === 'Update Patient';
})
.subscribe((activity) => {
console.log("Im editing the overview");
var _compState2
_compState2 = this.setPanelState(activity);
console.log('CompStateVar:'+_compState2)
})
}
render() {
const OverviewWrapper = this.state.COMPPROPS.compState === 0 ? OverviewEditPane: OverviewPane
return (
...
<Box colorIndex='light-2' direction='row' flex={false}>
<div>
<OverviewWrapper overview={this.state.PATIENT} ovtype={this.state.COMPPROPS} />
</div>
</Box>
I resolved this by keeping the existing design by only setting one value compState in the SetState value rather then trying to set both compName and compState. Once I did that everything seemed to work fine.

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