How to show navigation tab when it was already mounted - reactjs

I have hidden TabNavigator (react-navigation), and I need to show it after property event changed.
My component:
export default class Quotes extends Component {
static navigationOptions = ({ navigation }) => {
const { params } = navigation.state;
return {
tabBarVisible: params && params.showNavigation
};
};
UNSAFE_componentWillMount() {
this.props.navigation.setParams({ showNavigation: this.props.profileCreated });
}
render() {
...
}
}
I manipulate tabBarVisible with showNavigation option that connected to this.props.profileCreated. But I don't know where to move this code, to check every time props changed. When I'm trying to move it to render or componentWillReceiveProps it's not allowed to setState there.
Updated
When I'm trying to add:
static getDerivedStateFromProps(nextProps, prevState) {
if (!this.props.profileCreated && nextProps.profileCreated) {
this.props.navigation.setParams(
{ showNavigation: this.props.profileCreated }
);
}
}
I have next warnings:
ExceptionsManager.js:71 Warning: Quotes: Did not properly initialize state during construction. Expected state to be an object, but it was undefined.
...
Quotes.js:25 getDerivedStateFromProps
Quotes.js:26 {screenProps: undefined, navigation: {…}, unsubmitted: Array(0), quotes: {…}, fields: {…}, …}
ExceptionsManager.js:71 Warning:
Quotes.getDerivedStateFromProps(): A valid state object (or null) must be returned. You have returned undefined.

Note that you can use componentWillReceiveProps, but you just need to add an if statement to ensure you don't end up in the infinite update loop:
UNSAFE_componentWillReceiveProps(nextProps) {
if (!this.props.profileCreated && nextProps.profileCreated) {
this.props.navigation.setParams(
{ showNavigation: this.props.profileCreated }
);
}
}
However, it's recommended to use the static method getDerivedStateFromProps(nextProps, prevState) now. This method is called whenever a component gets updated (and also on initial mount).
static getDerivedStateFromProps(nextProps, prevState) {
if (!this.props.profileCreated && nextProps.profileCreated) {
this.props.navigation.setParams(
{ showNavigation: this.props.profileCreated }
);
}
return {} // or any state change you need
}

Related

How to reset state in a component on prop change

How should I reset the state of a Child on every prop change?
Parent component:
render() {
const { show, month } = this.props; // January, February, ...
return (
{ show ? <Child selectedMonth={month} /> : null }
);
}
Child component:
componentDidMount() {
this.resetChildState();
// this will never run if the Parent's month prop is changed
// only if show becomes false from true
}
I want resetChildState to run on every month change.
Just try to use componentDidUpdate, in the body of this method you can compare whether props from parent changed or not. So if they did just call your reset method or whatever you want.
For more info visit https://reactjs.org/docs/react-component.html#componentdidupdate
componentDidUpdate(prevProps, prevState) {
if(this.props.selectedMonth!== prevProps.selectedMonth) {
this.resetChildState();
}
}
You can use getDerivedStateFromProps()
static getDerivedStateFromProps(nextProps, prevState) {
if(monthChanged){
return initialState; //reset to initialState that you have defined.
}
return null;
}
if you want just reset your state exactly after changing the props, you can use the componentWillReceiveProps react lifecycle as below:
class Children extends React.Component {
// ...
state = {
name: "test"
};
componentWillReceiveProps(nextProps) {
this.setState({name: ""});
}
// ...
}
use ComponentDidUpdate
componentDidUpdate() {
if (this.props.id !== this.state.section_id) {
this.setState({
section_id:this.props.id,
filteredProducts:[],
productsByPage:[],
pageNumber:0,
hasMore:true,
showloading:true
},()=>this.fetchData());
}
}

child component does not rerender with shouldComponentUpdate

My child component changes its state when its prop is changed. I want to re-render child component ImageSlide so call different string when state changes. The className is well changed and console shows the changed values well. But it does not rerender view.
I tried shouldComponentUpdate, but it did not work.
How can I re-render ImageSlide?
let languages = {
en: require('textEnglish'),
kr: require('textKorean')
}
class ImageSlide extends Component {
constructor(props) {
super(props);
this.state={
lang: this.props.lang,
url: this.props.url
}
}
languageSelect=()=> {
if (this.state.lang === 'kr') {
return 'kr';
} else {
return 'en';
}
}
static getDerivedStateFromProps(nextProps, prevState){
if (nextProps.lang !== prevState.lang |
nextProps.url !== prevState.url )
{
return {lang : nextProps.lang, url: nextProps.url} ;
}
}
shouldComponentUpdate(nextProps, nextState){
return true;
//I tried also:
// if (nextProps.url !== this.state.url |
// nextProps.lang !== this.state.lang)
// return true;
}
render() {
const Text=languages[this.languageSelect()];
return (
<div className="test-transition">
{console.log(this.state.lang)}
{console.log(this.state.url)}
{console.log(Text["p1_3_"+String(this.state.url)])}
<div className={`pic${this.state.url}`}>
{Text["p1_3_"+String(this.state.url)]}
</div>
</div>
);
}
}
Check if the new props is changes or not using any of the lifecycle event like componentDidUpdate or shouldComponentUpdate or you can also try componentWillReceiveProps (not recommended since its deprecated)
componentDidUpdate()
{
/*Check for prev props condition*/
this.forceUpdate(); /*this method renders the component*/
}

How to migrate componentWillReceiveProps in react 16.0.0?

I have a reactcomponent that has a few obsolete events:
componentWillMount() {
const { applicationStages } = this.props;
if (applicationStages && applicationStages.length > 0) {
this.setState({
appColumnsSorted: this.getSortedAppColumns(someVar),
});
}
}
componentWillReceiveProps(nextProps) {
const {
presets: { sortCriteria: sortBy, customCriteria },
} = nextProps;
const { appColumnsSorted } = this.state;
const sortedColumnsUpdated = this.getSortedAppColumns(
appColumnsSorted,
sortBy,
true
);
this.setState({
appColumnsSorted: sortedColumnsUpdated,
});
}
getSortedAppColumns = (appColumns, sortBy, criticalFirst) => {
//returns object
};
'componentWillMount' is basically to initialize the appColumnsSorted. The issue is that with v16 this event is obsolete. So what can event can I use for this now? Also what is the way to migrate 'componentWillReceiveProps' in this scenario?
What you're using componentWillMount for can be done in the constructor so
componentWillMount() {
const { applicationStages } = this.props;
if (applicationStages && applicationStages.length > 0) {
this.setState({
appColumnsSorted: this.getSortedAppColumns(someVar),
});
}
}
will change to
export default class YourClass extends Component {
constructor(props) {
// keep a separate method just to avoid writing code in constructor for readability
this.state = constructInitialState(props);
}
constructInitialState(props) {
const state={};
//More state handling as required
const { applicationStages } = props;
if (applicationStages && applicationStages.length > 0) {
state.appColumnsSorted = this.getSortedAppColumns(someVar);
}
return state;
}
}
This approach is slightly better because getDerivedStateFromProps will be called before each render and will waste computation.
From the code snippet it is not obvious why you want to store it in state. If you do save it to state then the only way you have would be to use componentDidUpdate as mentioned in the other answer by Aaditya Thakkar. This will require you to mirror your props in state only for comparison purpose (Mapping props to state is not the best way, more on this link https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#anti-pattern-unconditionally-copying-props-to-state)
I would, however, recommend calling your sort computation method and return its result in render directly; this would avoid extra checks in componentDidUpdate to render the second time. As I'm not aware if these props are coming from redux or a parent React component, another option would be to avoid expensive computation in this class and simply provide the correct value from either the parent component or calculate the value in the redux store and send the final prop directly for use.
ComponentWillReceiveProps can be replaced with getDerivedStateFromProps. getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state. It's a static method, so this can not be used inside it.
Hence, you can no longer reference this.getSortedAppColumns from getDerivedStateToProps, you need componentDidUpdate lifecycle for that. Here, I have broken down ComponentWillReceiveProps into getDerivedStateFromProps and componentDidUpdate:
static getDerivedStateFromProps(nextProps, prevState) {
const {
presets: { sortCriteria: sortBy },
} = nextProps;
if (sortBy === prevState.sortBy) return null;
return ({ sortBy: nextProps.sortBy });
}
componentDidUpdate(_, prevState) {
const { appColumnsSorted, sortBy } = this.state;
if (sortBy !== prevState.sortBy) {
const sortedColumnsUpdated = this.getSortedAppColumns(
appColumnsSorted,
sortBy,
true
);
this.setState({
appColumnsSorted: sortedColumnsUpdated,
});
}
}

getDerivedStateFromProps doesn't work as expected

Im refactoring my react application and I replaced componentWillRecieveProps with getDerivedstateFromProps.
Then it gives an error saying Cannot read property 'setState' of undefined
This is my code
static getDerivedStateFromProps = nextProps => {
const { auth, history } = nextProps;
redirectIfAuthenticated(auth.isAuthenticated, history, './dashboard');
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
What am I doing wrong here?
getDerivedStateFromProps is not just a rename of componentWillReceiveProps, but has a different purpose and syntax. It doesn't have access to the class instance since it is static
Also getDerivedStateFromProps is supposed to only update state and not have any side-effects. All side-effects must go in componentDidUpdate
static getDerivedStateFromProps(props, state) {
if (props.errors !== state.prevErrors) {
return {
errors: props.errors,
prevErrors: props.errors
}
}
return { prevErrors: props.errors}
}
render() {
const { auth, history } = props;
if(this.props.auth.isAuthenticated) {
return <Redirect to={'/dashboard'} />
};
// extra logic here
}

Warning: setState(...): Cannot update during an existing state transition

I'm getting the following error:
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
My component:
import React from 'react';
import { Redirect } from 'react-router'
import Notifications from 'react-notification-system-redux';
constructor(props) {
super(props);
this.state = {
invite_token: this.props.match.params.token,
FormSubmitSucceeded: false,
inviteRequestSubmitSucceeded: false
};
}
....
inviteAlreadyUsed() {
const notificationOpts = {
message: 'Invitation already used!',
};
this.props.createNotificationSuccess(notificationOpts);
}
render() {
const { invite } = this.props;
if (invite && invite.status === "completed") {
this.inviteAlreadyUsed();
return <Redirect to={{ pathname: '/' }}/>;
}
...
Any suggestions on how to avoid this warning? Is this not how you would handle a redirect?
this.inviteAlreadyUsed(); in render -> reducer updating a state -> it call new render -> this.inviteAlreadyUsed(); -> reducer update a state and again and again...
Just don't call inviteAlreadyUsed in render.
First, I think you should bind the inviteAlreadyUsed() function. You can use arrow function () => {}.
inviteAlreadyUsed = () => {
const notificationOpts = {
message: 'Invitation already used!',
};
this.props.createNotificationSuccess(notificationOpts);
}
Second, seems like you set the state with props in constructor. Setting it in componentWillMount() might be a better approach.
constructor(props) {
super(props);
this.state = {
invite_token: '',
FormSubmitSucceeded: false,
inviteRequestSubmitSucceeded: false
};
}
componentWillMount() {
this.setState({
invite_token: this.props.match.params.token
})
}

Resources