How to update the state in mounting in reactjs - javascript

how to update the state after mounting or how to pass the state when moving from one page to another in react.
In My Scenario, by clicking button Add New, it directs to AddNew page, in that clicking on save will redirect to display page, that works.
when i move to addnew page
In Display multiselect remain same(always en), how to get rendered state after redirecting
class Display extends React.PureComponent{
constructor(props) {
super(props);
}
componentWillMount() {
console.log(this.state.language);
const defLanguage = this.props.loginData.language; // "en"
this.setState({ language: defLanguage.split(",") }, () =>
this.callQueryApiForFetch("ONLOAD")
);
}
render(){
<MultiSelect
filterOptions={formatLanguageArray(
languageConfig.pvalues
)}
setSelectedFieldValues={value => {
this.setState({ language: value }, () => {
this.callQueryApiForFetch("ONLOAD");
});
}}
id="language"
itemsSelected={this.state.language}
label="Select Language"
/>
<button className="page-header-btn icon_btn display-inline">
<img
title="edit"
className="tableImage"
src={`${process.env.PUBLIC_URL}/assets/icons/ic_addstore.svg`}
/>
{`Add New`}
</button>
}
}
class AddNew extends React.Component {
constructor(props) {
super(props);
}
componentWillReceiveProps = () => {
const defLanguage = this.props.location.state.language;
this.setState({ language: defLanguage });
}
render(){
<Link
to={{
pathname: "/ui/product-info",
state: {
language: this.state.language
}
}}
className="btn btn-themes btn-rounded btn-sec link-sec-btn"
>
Save
</Link>
}

Since you mentioned to redirect to next page , you can also try this.props.history.push("/report") here "/report" is the link you want to redirect to.
You can check React-Routing Concept (just a suggestion)
For sending props from parent component to child component
your case :
componentWillReceiveProps(nextProps){
if(nextProps.someValue!==this.props.someValue){
//Perform some operation
this.setState({someState: someValue });
this.classMethod();
}
}

It is a good practice in React to separate state management from component rendering logic. The idea is that you pass fragments of a centralized state to your component hierarchy, from top to bottom, and allow them to use these data in their render method. In this approach you avoid keeping application state within the components.
Whenever a component needs to change the application state, it dispatches a message that is processed by a function called "reducer", and updates the state in the store. The whole concept is based on having a single source of truth for the entire application, and preventing components from manipulating the store directly.
The standard way to implement this is to use a design pattern called Redux, which is made available to React apps through the react-redux library.
I suggest you take a look at this tutorial to learn how you use the React Redux library in practice.

1.make the Display as Component and not PureComponent.
2.define a local state in Display
// default state..
state = {
language: 'us',
}
3.console whether your'e getting language in Display's props(will mount).
//probably inside
this.props.location.state
4.then set the state
this.setState(prevState => ({
...prevState,
language: --value--,
})

Related

how to pass the state property as prop to show and hide component in react when it's asynchronous update

i have a navbar when i click on it i'm setting my state to true and passing it in my component as prop but the function takes time update it and my props are sent as false how can i send my updated prop when the state update is complete
navbar.js
constructor(props) {
super(props);
this.state = {
burgerClicked:false,
};
}
burgerClicked=()=>{
this.setState({ burgerClicked: true },()=>{ this.props.clicked(this.state.burgerClicked)})
}
app.js
constructor(props){
super(props);
this.state={
open:false,
}
}
openSideNav =(burgerClicked)=>{// the time is taking here to update it send open as false
this.setState({open:burgerClicked},()=>{
console.log(this.state.open)
});
<Navbar clicked={this.openSideNav} tokenRequested={this.request}/>
<Header open={this.state.open} />
header.js
constructor(props) {
super(props);
this.state = { showSideNav: false };
}
UNSAFE_componentWillReceiveProps(){
//
console.log(this.props.open,this.state.showSideNav);//this.props.open is false
if (this.props.open) {
this.setState({showSideNav:this.props.open},()=>{
console.log(this.state.showSideNav); //this.state.showSideNav dont print on first click but print true on second click on burger
})
}
console.log(this.props.open,this.state.showSideNav); //this.props.open is false
}
{(() => {
if (!this.state.showSideNav) {
return null;
}
return(
<SideNavbar closeNav={this.closeSideNav}/>
)
})()}
This answer may be more than what you are asking for, but here it goes:
In this example you have three states: burgerClicked (in Navbar.js), open (in App.js), and showSideNav (in header.js).
It looks like all three of those keep track of the same thing: "is the side nav open?". You don't want that, you should have only one component that keeps track of that.
Based on your example, I would say that only App.js should have that state, and pass its value to Navbar and header as props.
In Navbar.js, the onClick just needs to call the clicked function, without passing any param. Then, App.js figures out whether to set open to true or false, (which most likely just means toggling it).
Once App.js updates its state, it will pass that updated value down to header and Navbar and that's the beauty of React.
Your Question is kinda not understandable and only can figure out you want to use conditional rendering.
Read this https://reactjs.org/docs/conditional-rendering.html
Hope it will help you :)

setState() on root container doesn't keep routes of react-navigation (V3)

I have a root Component (App) which renders a nested navigation as shown in the code.
Inside of app, I have the user object (stored in state) which is used by all child objects. It contains information about the groups you're in.
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
user: null
}
}
render() {
if (!this.state.user) {
// from the login component, I make an API call to my backend.
// If the user logs in, I do app.setState({user: loggedInUser}) from inside the LoginComponent
return <LoginComponent app={this} />
}
return createAppContainer(createSwitchNavigator({
MainApp: {
screen: createBottomTabNavigator({
StartPage: {
screen: ({ navigation }) => {
return <StartPage navigation={navigation} app={this} />
}
},
Groups: {
screen: createStackNavigator({
ListGroups: {
// When clicking on a Group from the GroupsPage, I execute:
// this.props.navigation.navigate('GroupDetail', { group, app })
// (from inside the GrousPage screen)
screen: ({ navigation }) => <GroupsPage navigation={navigation} app={this} />
},
GroupDetail: {
// Inside the GroupDetail, you can leave or join a group.
// If you do that, I'm making an API call and get the new user object.
// Then, I do this.props.navigation.getParam('app').setState({user: newUser})
// "app" was passed from ListGroups
screen: GroupDetail
}
})
}
})
}
}))
}
}
Now, when I'd like to set the updated user (from GroupDetail via app.setState({user: newUser})), the navigation doesn't keep its route and goes back to StartPage.
However, what I wanted instead is to re-render GroupDetail with the new user.
How would I be able to keep the navigation state? Or do you think I have a design flaw and any idea on fixing it? Thanks!
(Yes, I'd like to keep the user object only inside of App and pass it down. Main reason is to only have few API calls)
you should not create a new navigator on authentication but instead add a route to your navigator, which will be your default route and this route will take care of your login.
It should be passed a callback to set the user like this:
setUser = user => this.setState({user});
return <AuthPage setUser={this.setUser} /> // You also don't have to pass navigation. It will be passed automatically.
Inside your AuthPage you can set the user with
this.props.setUser(newUser);
and navigate to your desired route with the navigation prop or just go back to the previous route:
this.props.navigation.navigate('App');
Or this.props.navigation.goBack();
To keep user logged in you should check if the user is logged in in your Auth route and react accordingly:
componentDidMount() {
const user = getUser(); // This depends on how you persist the user
this.props.navigation.navigate(user ? 'App' : 'Auth');
}
Hope this helps.
According to docs, you have to persist the user navigation state.
read more here.

Sync local react state with data from redux

I have an App component which contains all the routes
App.jsx
import Editor from './Editor';
function App(props) {
return (
<BrowserRouter>
<Switch>
<Route path="/cms/post/edit/:postId" component={Editor} />
</Switch>
</BrowserRouter>
);
}
export default App;
I have an Editor component where user can edit a post. This component maps the data from redux store to local state as data needs to be manipulated locally.
Editor.jsx
// Usual Imports
class Editor extends Component {
constructor(props) {
super(props);
this.state = {
title: props.post ? props.post.title : '',
content: props.post ? props.post.content : ''
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.props.post ? null : this.fetchPosts(this.props.match.params.postId);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
updatePost(data) {
// function to updatePost
}
fetchPosts(id) {
// function to fetch Posts
}
render() {
return (
<React.Fragment>
<input type="text" name="title" value={this.state.title} onChange={this.handleChange} />
<input type="text" name="content" value={this.state.content} onChange={this.handleChange} />
</React.Fragment>
);
}
}
const mapStateToProps = (state, ownProps) => ({
post: state.posts[ownProps.match.params.postId] || false,
...ownProps
}),
mapDispatchToProps = dispatch => ({
updatePost: data => dispatch(updatePost(data)),
fetchPosts: params => dispatch(fetchPosts(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(Editor);
Now my questions are
Initially, post data is available and fetchPosts is not called. However, if user refreshes the page then post becomes false and fetchPosts is called and redux store is updated.
In which lifecycle method should I update the local react state with data from props?
Possible solutions which I think could be
A. Updating state in componentWillReceiveProps
componentWillReceiveProps(nextProps) {
this.setState({
title: nextProps.post.title,
content: nextProps.post.content
});
}
However, React docs discourages using componentWillReceiveProps as it might be invoked many times in React 16 and so on.
B. Update state in componentDidUpdate
componentDidUpdate(prevProps, prevState) {
if (this.props.post != prevProps.post) {
this.setState({
title: this.props.title,
content: this.props.content
});
}
}
I am not sure about this as I think there might be hidden side effects of this approach.
C. Not setting initial state and providing value to input tags via props i.e. value={this.props.post.title} and updating state via onChange handlers. However, when value is undefined then React will throw an error.
D. Newer lifecycle methods like getDerivedStateFromProps. Not too sure as React docs says it should be used in rare cases when state changes based on props over time.
I need to maintain the state as current component is also used for creating a fresh post also.
Which approach would be the best? And if I am missing out on something then let me know! Thanks!

ReactJS ComponentWillMount() after passing Props

I want to ask why the child ( ComponentWillMount() ) component is only once rendered, once I am passing props to it everytime on onClick.
Once I click some button that is passing props to the child, the ComponentWillMount() of child is not triggering again, only in the first click only.
Parent Component:
render(){
return(
<div>
<AppModuleForm
editID = {this.state.editID}
editURL = {this.state.editURL}
editConf = {this.state.editConf}
editDesc = {this.state.editDesc}
editIcon = {this.state.editIcon}
editParent = {this.state.editParent}
editOrder= {this.state.editOrder}
status={this.state.status}
moduleList={this.state.moduleList}
updateAppModuleTree={this.updateAppModuleTree.bind(this)}/>
</div>
)
}
Child Component:
constructor(props){
super(props)
console.log(this.props.editDesc)
this.state={
url:'',
description:'',
parentID:'',
order:'',
configuration:'',
icon:'',
parentIDList:[],
urlDuplicate: false,
isSuccess: false,
errorMessage: '',
}
}
componentWillMount(){
if(this.props.status==='edit'){
let {
editURL,
editDesc,
editParent,
editConf,
editIcon,
editOrder} = this.props
this.setState({
url:editURL,
description:editDesc,
parentID:editParent,
order:editOrder,
configuration:editConf,
icon:editIcon,
})
}
}
componentWillReceiveProps(nextProps){
if(nextProps.status != this.props.status){
if(this.props.status==='edit'){
let {
editURL,
editDesc,
editParent,
editConf,
editIcon,
editOrder} = this.props
this.setState({
url:editURL,
description:editDesc,
parentID:editParent,
order:editOrder,
configuration:editConf,
icon:editIcon,
})
}
}
}
ComponentWillMount is mounting lifecycle method which will be called before mounting your component hence initialisation can be done in that while ComponentWillReceiveProps will be called once props are changed and you will get changes in nextProps parameter.
First you need to read https://reactjs.org/docs/state-and-lifecycle.html
and understand where to use props and why you need to pass something into component state.
From http://lucybain.com/blog/2016/react-state-vs-pros/
So when would you use state?
When a component needs to keep track of information between renderings
the component itself can create, update, and use state.
So you shouldn't transfer to state anything that will not change internally during component live cycle. As I can see all props those you pass to component are most likely will not be changed from within the component, all callbacks and icons you should take from props in component jsx.
If you have some editable data that you pass into its props from parent, on component mount (use componentWillMount()) you can copy that data to component state.That means all data will be stored in component internally and will not being overwritten on every render() call from passed props.
If you need to check if new props contains changes you can use componentWillReceiveProps(newProps) and there you can compare newProps with this.props and and process changes if needed.
Also i can suggest you to rename component callbacks handlers with respect to naming best practices:
<div>
<AppModuleForm
handleEditID = {this.onEditID}
handleEditURL = {this.onEditURL}
handleEditConf = {this.onEditConf}
handleEditDesc = {this.onEditDesc}
handleEditIcon = {this.onEditIcon}
handleEditParent = {this.onEditParent}
handleEditOrder= {this.onEditOrder}
status={this.state.status}
moduleList={this.state.moduleList}
updateAppModuleTree={this.updateAppModuleTree.bind(this)}/>
</div>
And I dont see any reasonable purpose to declare or to store functions in components state. So you can consider to move your handlers this.state.editID
etc. to parent component this scope. Like that
onEditId = () => { /* function code */ }
If you use arrow function = () => it automatically binds to component this and you don't need to bind them manually like you do in
{this.updateAppModuleTree.bind(this)}
After all that may be you will understand more clearly how you should manage your components life cycle and your problem will no longer be relevant.

React Router v4 previous state component

I'm looking for a solution - react router v4 doesn't hold the previous state of component. For example, here is a simple counter:
import React, { Component } from "react";
class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
this.holdState = this.holdState.bind(this);
}
holdState() {
this.props.location.state = this.state.counter;
const state = this.state;
this.setState({
counter: state.counter + 1
});
}
render() {
return (
<div>
<ul>
<li>6/5 # Evergreens</li>
<li>6/8 vs Kickers</li>
<li>6/14 # United</li>
</ul>
<div>
<button onClick={() => this.holdState()}>Click</button>
<span>{this.state.counter}</span>
</div>
</div>
);
}
}
export default Schedule;
I was trying to push the state into location and history by props.
But whenever I press "browser button back" from the next page, it always resets the state.
Can someone tell me where I'm doing a mistake?
Whenever your component mounts, your constructor will be initiated which will reset the state back to 0. This happens when you are on another component and press back button in which case your current Route gets mounted.
Also directly mutating the location.state value is not a right approach. What you need is to save your value in localStorage and refill it in state if it exists.
Another thing is that when you wish to update the state based on prevState, you could make use of functional setState. Check this for more details:
When to use functional setState
import React, { Component } from "react";
class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
counter: localStorage.getItem('counter') || 0
};
this.holdState = this.holdState.bind(this);
}
holdState() {
localStorage.setItem('counter', this.state.counter);
this.setState(prevState => ({
counter: prevState.counter + 1
}));
}
render() {
return (
<div>
<ul>
<li>6/5 # Evergreens</li>
<li>6/8 vs Kickers</li>
<li>6/14 # United</li>
</ul>
<div>
<button onClick={() => this.holdState()}>Click</button>
<span>{this.state.counter}</span>
</div>
</div>
);
}
}
export default Schedule;
I was trying to push state into location and history by props. BBut whenever I > press "browser button back" from the next page, it always resets the state.
when you call setState you're not changing the route, you're just triggering a rerender of your component.
If you press the back button after incremented the counter react router will symply pop the last route from the history stack but since you don't have a previous route the component will be remount hence the resetted state.
To implement what i suppose you want to achieve you need to explicitely change the route every setstate (e.g. adding a parameter in the query string with the current value of the counter, like ?counter=1, ?counter=2..) , this way you'll be sure that a new route will be push on top of the stack every setState and the back button will then work as you expect.

Categories

Resources