safe alternative to UNSAFE_componentWillMount? - react-native

I have an app that uses class based components. In the process of converting it from flow to typescript in line with current trends I realised that componentWillMount has ben deprecated and is no longer safe to use. I'm not sure what to do with it as this is in a class based component and converting it all into function components will take too long. I tried componentDidMount, but I got an error saying that setState should not be used in componentDidMount:
UNSAFE_componentWillMount() {
this._setupPanResponder();
this.setState({ date: this.props.date });
}
Is there a safe alternative to this that doesn't involve changing the component to a function component?

Can you move it to constructor?
Something like:
...
constructor(props: PropsType) {
super(props);
this._setupPanResponder();
this.state = { date: props.date };
}
...

Related

how am I breaking rules of hook? [duplicate]

In this example, I have this react class:
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
The question is if I can add React hooks to this. I understand that React-Hooks is alternative to React Class style. But if I wish to slowly migrate into React hooks, can I add useful hooks into Classes?
High order components are how we have been doing this type of thing until hooks came along. You can write a simple high order component wrapper for your hook.
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useMyHook();
return <Component {...props} myHookValue={myHookValue} />;
}
}
While this isn't truly using a hook directly from a class component, this will at least allow you to use the logic of your hook from a class component, without refactoring.
class MyComponent extends React.Component {
render(){
const myHookValue = this.props.myHookValue;
return <div>{myHookValue}</div>;
}
}
export default withMyHook(MyComponent);
Class components don't support hooks -
According to the Hooks-FAQ:
You can’t use Hooks inside of a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
As other answers already explain, hooks API was designed to provide function components with functionality that currently is available only in class components. Hooks aren't supposed to used in class components.
Class components can be written to make easier a migration to function components.
With a single state:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { state } = this;
const setState = state => this.setState(state);
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [state, setState] = useState({sampleState: 'hello world'});
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
Notice that useState state setter doesn't merge state properties automatically, this should be covered with setState(prevState => ({ ...prevState, foo: 1 }));
With multiple states:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { sampleState } = this.state;
const setSampleState = sampleState => this.setState({ sampleState });
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
Complementing Joel Cox's good answer
Render Props also enable the usage of Hooks inside class components, if more flexibility is needed:
class MyDiv extends React.Component {
render() {
return (
<HookWrapper
// pass state/props from inside of MyDiv to Hook
someProp={42}
// process Hook return value
render={hookValue => <div>Hello World! {hookValue}</div>}
/>
);
}
}
function HookWrapper({ someProp, render }) {
const hookValue = useCustomHook(someProp);
return render(hookValue);
}
For side effect Hooks without return value:
function HookWrapper({ someProp }) {
useCustomHook(someProp);
return null;
}
// ... usage
<HookWrapper someProp={42} />
Source: React Training
you can achieve this by generic High order components
HOC
import React from 'react';
const withHook = (Component, useHook, hookName = 'hookvalue') => {
return function WrappedComponent(props) {
const hookValue = useHook();
return <Component {...props} {...{[hookName]: hookValue}} />;
};
};
export default withHook;
Usage
class MyComponent extends React.Component {
render(){
const myUseHookValue = this.props.myUseHookValue;
return <div>{myUseHookValue}</div>;
}
}
export default withHook(MyComponent, useHook, 'myUseHookValue');
Hooks are not meant to be used for classes but rather functions. If you wish to use hooks, you can start by writing new code as functional components with hooks
According to React FAQs
You can’t use Hooks inside of a class component, but you can
definitely mix classes and function components with Hooks in a single
tree. Whether a component is a class or a function that uses Hooks is
an implementation detail of that component. In the longer term, we
expect Hooks to be the primary way people write React components.
const MyDiv = () => {
const [sampleState, setState] = useState('hello world');
render(){
return <div>{sampleState}</div>
}
}
You can use the react-universal-hooks library. It lets you use the "useXXX" functions within the render function of class-components.
It's worked great for me so far. The only issue is that since it doesn't use the official hooks, the values don't show react-devtools.
To get around this, I created an equivalent by wrapping the hooks, and having them store their data (using object-mutation to prevent re-renders) on component.state.hookValues. (you can access the component by auto-wrapping the component render functions, to run set currentCompBeingRendered = this)
For more info on this issue (and details on the workaround), see here: https://github.com/salvoravida/react-universal-hooks/issues/7
Stateful components or containers or class-based components ever support the functions of React Hooks, so we don't need to React Hooks in Stateful components just in stateless components.
Some additional informations
What are React Hooks?
So what are hooks? Well hooks are a new way or offer us a new way of writing our components.
Thus far, of course we have functional and class-based components, right? Functional components receive props and you return some JSX code that should be rendered to the screen.
They are great for presentation, so for rendering the UI part, not so much about the business logic and they are typically focused on one or a few purposes per component.
Class-based components on the other hand also will receive props but they also have this internal state. Therefore class-based components are the components which actually hold the majority of our business logic, so with business logic, I mean things like we make an HTTP request and we need to handle the response and to change the internal state of the app or maybe even without HTTP. A user fills out the form and we want to show this somewhere on the screen, we need state for this, we need class-based components for this and therefore we also typically use class based components to orchestrate our other components and pass our state down as props to functional components for example.
Now one problem we have with this separation, with all the benefits it adds but one problem we have is that converting from one component form to the other is annoying. It's not really difficult but it is annoying.
If you ever found yourself in a situation where you needed to convert a functional component into a class-based one, it's a lot of typing and a lot of typing of always the same things, so it's annoying.
A bigger problem in quotation marks is that lifecycle hooks can be hard to use right.
Obviously, it's not hard to add componentDidMount and execute some code in there but knowing which lifecycle hook to use, when and how to use it correctly, that can be challenging especially in more complex applications and anyways, wouldn't it be nice if we had one way of creating components and that super component could then handle both state and side effects like HTTP requests and also render the user interface?
Well, this is exactly what hooks are all about. Hooks give us a new way of creating functional components and that is important.
React Hooks let you use react features and lifecycle without writing a class.
It's like the equivalent version of the class component with much smaller and readable form factor. You should migrate to React hooks because it's fun to write it.
But you can't write react hooks inside a class component, as it's introduced for functional component.
This can be easily converted to :
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div>{sampleState}</div>
}
It won't be possible with your existing class components. You'll have to convert your class component into a functional component and then do something on the lines of -
function MyDiv() {
const [sampleState, setSampleState] = useState('hello world');
return (
<div>{sampleState}</div>
)
}
For me React.createRef() was helpful.
ex.:
constructor(props) {
super(props);
this.myRef = React.createRef();
}
...
<FunctionComponent ref={this.myRef} />
Origin post here.
I've made a library for this. React Hookable Component.
Usage is very simple. Replace extends Component or extends PureComponent with extends HookableComponent or extends HookablePureComponent. You can then use hooks in the render() method.
import { HookableComponent } from 'react-hookable-component';
// πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡
class ComponentThatUsesHook extends HookableComponent<Props, State> {
render() {
// πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡
const value = useSomeHook();
return <span>The value is {value}</span>;
}
}
if you didn't need to change your class component then create another functional component and do hook stuff and import it to class component
Doesn't work anymore in modern React Versions. Took me forever, but finally resulted going back to go ol' callbacks. Only thing that worked for me, all other's threw the know React Hook Call (outside functional component) error.
Non-React or React Context:
class WhateverClass {
private xyzHook: (XyzHookContextI) | undefined
public setHookAccessor (xyzHook: XyzHookContextI): void {
this.xyzHook = xyzHook
}
executeHook (): void {
const hookResult = this.xyzHook?.specificHookFunction()
...
}
}
export const Whatever = new WhateverClass() // singleton
Your hook (or your wrapper for an external Hook)
export interface XyzHookContextI {
specificHookFunction: () => Promise<string>
}
const XyzHookContext = createContext<XyzHookContextI>(undefined as any)
export function useXyzHook (): XyzHookContextI {
return useContext(XyzHookContextI)
}
export function XyzHook (props: PropsWithChildren<{}>): JSX.Element | null {
async function specificHookFunction (): Promise<void> {
...
}
const context: XyzHookContextI = {
specificHookFunction
}
// and here comes the magic in wiring that hook up with the non function component context via callback
Whatever.setHookAccessor(context)
return (
< XyzHookContext.Provider value={context}>
{props.children}
</XyzHookContext.Provider>
)
}
Voila, now you can use ANY react code (via hook) from any other context (class components, vanilla-js, …)!
(…hope I didn't make to many name change mistakes :P)
Yes, but not directly.
Try react-iifc, more details in its readme.
https://github.com/EnixCoda/react-iifc
Try with-component-hooks:
https://github.com/bplok20010/with-component-hooks
import withComponentHooks from 'with-component-hooks';
class MyComponent extends React.Component {
render(){
const props = this.props;
const [counter, set] = React.useState(0);
//TODO...
}
}
export default withComponentHooks(MyComponent)
2.Try react-iifc: https://github.com/EnixCoda/react-iifc

React Native - What's the different about this.state.data with this.data?

I was confused what's the different about this.state.data with this.data
let say I have a code like this:
componentWillMount(){
console.log(this.props.navigation.state.params.list);
api.get('my API Url')
.then((response)=> {
this.setState({data: JSON.parse(response.data)[0]})
this.data=JSON.parse(response.data)[0]
})
.catch((err)=>{
console.log("axios catching error")
Alert.alert("failed", "Retry to retrieve from API", [{text:'OK', onPress:()=>{this.componentWillMount()}}])
console.log(err)
})
}
constructor(props){
super(props);
this.state ={ data:[] }
this.data=[]
}
class Visit extends React.Component {
render() {
if (this.data.length==0){
return(
<Loader/>
)
}
return (
<Text>Visit</Text>
);
}
}
export default Visit;
with above code, I can't render <Text>Visit</Text> when this.data already have an array, but with this.state.data my App can rendering <Text>Visit</Text>,
so I want to know the different about this.state.data with this.data,
anyone can explain me?
this.state.data refers to data property of the current class state, while this.data refers to data property of current class.
A component's State is a trivial part of React environment, and if you can't understand this difference I suggest you to check this.
I also suggest you to check react official docs, almost everything from React applies to React Native, for me, React Native is almost the same as React, the only difference is you have more limited components in RN. We could say that RN is a subset of React
Note: property or properties it's not the same than component's props defined in React specs
For the purpose of React, it would be best to make use of the state. this.state.data refers to the data prop stored in the current component state. You can set the value by calling the setState method. On the other end, this.data refers to the data prop of the current class. You could change its value like you would other class variables in Javascript.

What is the <{}> syntax after extends Component?

I started a new project today using React Native 0.51.0 and noticed that the class syntax for the default project file had something new added, the <{}> syntax after extends Component:
export default class App extends Component<{}> {
...
}
I tried doing research but most search engines ignore special characters even with exact string matching, so trying to find out what this syntax is has proved to be difficult. I did some testing and was able to figure out that this change appeared in v0.49.0. The release notes make no mention of what this added syntax does though.
A lot of vague keyword searching and reading leads me to believe that this may be syntax related to TypeScript, but being unfamiliar with the language, I'm at a loss as to how to search and find out more about the syntax without knowing what the proper term for it is. Could anyone tell me what the name of the syntax and what it does? Specifically with regards to React Native.
It is related to Flow typings for the props you will receive in the component. Component<{}> would mean that you don't expect the component to receive props.
With Flow and React.Component, you can define types for props and state (see React$Component type declaration for details).
Example from Flow documentation about React components
import * as React from 'react';
type Props = { /* ... */ };
type State = {
count: number,
};
class MyComponent extends React.Component<Props, State> {
state = {
count: 0,
};
componentDidMount() {
setInterval(() => {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}, 1000);
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
<MyComponent />;

this.state within react-navigation screen

I'm having trouble accessing and defining state. My app uses react-navigation. Overall within my app I can always work with state (without errors), but within the primary tabbar screens, I get a "null is not an object" error when I use a simple definition of state like I do below.
I am not using redux
export class Review_Screen extends React.Component {
// set title at the top of the page
static navigationOptions = ({navigation}) => ({
title: navigation.state.params.title
});
constructor(props) {
super(props);
const { params } = this.props.navigation.state;
this.state = {
// general ID info
barcode: params.productdata.barcode,
userID: params.user.ID,
username: params.user.name,
expanded: false,
testing: 'hallo hallo',
};
}
render() {
console.log('-_-_-_-_-_-_-_-_-_-_-_-_-');
console.log(this.state.testing);
console.log('-_-_-_-_-_-_-_-_-_-_-_-_-');
const { params } = this.props.navigation.state;
etc...
// results in error "null is not an object (evaluating 'this.state.testing')
I assume this is because I am supposed to work differently with state when I am within react-navigation.
How do I define some local state variables? What is best practice? I will at some point go to redux, but am not ready for that yet.
Well, that's embarrassing. The issue arose because I had my react-native on "hot reloading", while I was coding.
This basically means that I was asking for a variable that wasn't defined, because the constructor wasn't called (the app was live while coding).
Once I restart or reload the app, then it does define the state (because while initially loading the app it calls the constructor().
Hope this serves someone else..

The child component is not called again if the component name is same

I have a scenario where I have to call same component consecutively. First time when I call the component it loads but second time when I call the same component it doesn't reload the component.
I am using change state method to change the component.
ChangeState: function (newState) {
app.state = newState;
},
If I call some other component in between then it works correctly. Please suggest how can i solve this issue.
Try the below code. it will work if $ is not supported.
Vue.nextTick(function() {
app.state = newState;
})
No, it won't if Vue is watching for changes. One way around this is to set your state to null, then reset your component in the nextTick:
ChangeState(newState) {
this.state = null;
this.$nextTick(() => {
this.state = newState;
});
}
Here's the JSFiddle: https://jsfiddle.net/1spj1hzv/
Your code is a bit strange though, what is app? You don't seem to be referring to any internal data property (like I have in my answer), but your question suggests that app.state is reactive, so you may need to tweak this answer for your particular use case.