Update state values with props change in reactjs - reactjs

I have a modal component that should be called when setState changes but for some reason it is not updating. In the first file Im setting the following in render.
<ModalParcel showModal={this.state.showModal} parcelToConfirm={this.state.dataForParcelToConfirm} />
Within the Modal component constructor() I am setting the following.
constructor(props) {
super(props);
console.log("Constructor for modal componenet called.")
//this.renderRowForMain = this.renderRowForMain.bind(this);
this.state = {
show: this.props.showModal
};
}
render() {
if(this.state.show == true){
var content =
<View style={styles.modal}>
<View style={styles.modalContent}>
<TouchableHighlight onPress={() => this.closeModal()}>
<Text>Close</Text>
</TouchableHighlight>
</View>
</View>;
}else {
var content = null;
}
return ( content );
}
The problem is that the constructor() only gets called once on initial creation. I was under the impression that when I update the state to show the modal the constructor would be called again but this isn't happening.
I basically want to change the state to show the modal and then re-run render().

No constructor will not get called when component re-rendered, it will get called only on initial rendering.
I think, there are two ways by which you can solve your issue:
1. Use componentWillReceiveProps(){ lifecycle method it will get called whenever any change happens to props values, so you can update the Modal component state value showModal here, like this:
componentWillReceiveProps(nextProp){
this.setState({
show: nextProps.showModal
});
}
As per DOC:
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.
2. Don't store the props value in state variable and directly use this.props.showModel in Model component, by this way whenever any change happens to props values, Modal component will take the updated values.

Related

How to use state in dynamically created components at runtime for React Native

I have created a function that creates and builds a tree of components at run time.
This tree is different every time based on some parameters coming from a webservice.
I am able to render the tree successfully, but the state values don't get updated when the state is changed.
For example:
export default class App extends React.Component {
state={
loading:true,
}
componentDidMount() {
this.buildComponent();
}
buildComponent() {
var comp = <View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Text>{this.state.name}</Text>
<Button title="Change State" onPress={()=>this.setState({name:"Changed"})} />
</View>
this.comp = comp;
this.setState({loading:false});
}
render() {
if (this.state.loading) {
return <Text style={{alignSelf:'center',marginTop:50}}>Loading ...</Text>
}
return (
this.comp
);
}
}
In this code sample, I know it is not best practice to do it this way, but I just have a use case in which I need to code it in such way.
As you see in the sample, when I click on the button, I want the state name to be changed to "Changed", but it doesn't get changed.
Sounds like React doesn't map the state.name inside the text and renders it once it's changed.
I don't want to place the buildComponent inside render method because buildComponent does a lot of expensive calculations and calling it every time would affect the performance big time, therefore I wanted a way to build the components one time and render it, but state values don't get updated at all.
Thanks in advance for your valuable input.
u are using componentDidMount so the comp only renders once when the state is updated componentDidMount is not called, so if u want to reRender comp again u def componentDidUpdate instead
componentDidUpdate() {
this.buildComponent();
}

Strange problem using conditional rendering of component

I have a simple React component that takes in a single prop, if this prop is 0 show one component otherwise show another.
On page load it equals 0 so the first component loads, once my Redux store is updated from localstorage this value in the parent component updates its local state passing down the new prop to my component.
What happens is rather than switching to render my other component it puts the second component inside of the first... I have never seen this behaviour before and cannot for the life of me figure out why.
I have tried all sorts, switching between stateless and functional components, I have tried using componentWillReceiveProps to receive the updated prop and setState based on this and have my conditional logic based on state. All with the same results.
const ComponentThatGetsUpdated = (props) => (
props.PropThatGetsUpdated === 0 ? <ComponentOne /> : <ComponentTwo />
);
I expect on page load the output to be the contents of
<div>contents of component one</div>
then once my redux store has updated the new props are passed down the output should now be the contents of component two
<div>contents of component two</div>
The actual output once props have been updated is
<ComponentOne />
<ComponentTwo></ComponentTwo>
<ComponentOne />
FYI the parent component of ComponentThatGetsUpdated looks like the below:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {PropToPassDown: 0}
this.sub = this.sub.bind(this);
const sub = store.subscribe(this.sub);
}
sub() {
//when my redux store is updated this function runs and sets PropToPassDown to the new value, for this example it sets PropToPassDown in local state to 1
}
render() {
return (
<ComponentThatGetsUpdated PropToPassDown={this.state.PropToPassDown} />
)
}
}

How to refresh props with React/Redux when user enters a container

I have CompetitionSection which repeats all the competitions from database. When user clicks on one, it redirects him to a Competition Page, loads for a second and renders the page with all the details in it. So far, so good.
But when users goes back to the Competition Section and then click on the second competition, it instantly loads up the previous competition, 0 loading time.
From my point of view, what is failing is that the props of the component are not updating when I render the component (from the second time). Is not a router problem, which was my first instinct because I'm seeing the route.params changing acordingly, but the actions I dispatch to change the props are not dispatching. Here's a bit of code of said component.
class CompetitionPage extends React.Component {
componentWillMount() {
let id = getIdByName(this.props.params.shortname)
this.props.dispatch(getCompAction(id));
this.props.dispatch(getCompMatches(id));
this.props.dispatch(getCompParticipants(id));
this.props.dispatch(getCompBracket(id));
}
render() {
let { comp, compMatches, compBracket, compParticipants } = this.props
...
I tried every lifecycle method I know. component Will/Did Mount, component Will/Did update and I even set shouldUpdate to true and didn't do the trick. As I understand, the problem will be solved with a lifecycle method to dispatch the actions everytime an user enters Competition Page and not just for the first time. I'm running out of options here, so any help will be appreciated.
NOTE: I'm a newbie at React/Redux so I KNOW there are a couple of things there are anti-pattern/poorly done.
UPDATE: Added CompetitionsSection
class CompetitionsSection extends React.Component {
render() {
const {competitions} = this.props;
return (
...
{ Object.keys(competitions).map(function(comp, i) {
return (
<div key={i} className={competitions[comp].status ===
undefined? 'hide-it':'col-xs-12 col-md-6'}>
...
<Link to={"/competitions/"+competitions[comp].shortName}>
<RaisedButton label="Ver Torneo" primary={true} />
</Link>
...
It helps to better understand the lifecycle hooks. Mounting a component is when it is placed on the DOM. That can only happen once until it is removed from the DOM. An UPDATE occurs when new props are passed or setState is called. There are a few methods to troubleshoot when updates are not happening when you think they should:
Ensure that you are changing state in componentDidMount or componentDidUpdate. You cannot trigger an update in componentWillMount.
Make sure that the new props or state are completely new objects. If you are passing an object down in props and you are just mutating the object, it will not trigger an update. For instance, this would not trigger a update:
class CompetitionPage extends React.Component {
constructor(props) {
super(props)
this.state = {
competitions: [ compA, compB ]
}
}
triggerUpdate() {
this.setState({
competitions: competitions.push(compC)
})
}
componentDidMount() {
triggerUpdate()
}
render() {
return(
<div>
Hello
</div>
)
}
This is due to the fact that a new competition is being appended to the array in state. The correct way is to completly create a new state object and change what needs to be changed:
const newCompetitions = this.state.competitions.concat(compC)
this.setState(Object.assign({}, this.state, { competitions: newCompetitions }))
Use ComponentWillRecieveProps on an update to compare previous and current prop values. You can setState here if clean up needs to be done:
Read more about this method in the React documentation:
https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops

ReactJS Is it adviceable to update props

Is it adviceable to change the props to load my page with fresh data.
I have a requirement where I need to load the HTML page from JSON, example given below
formSchema:{
"label": "Step 1",
"action": "Next",
"Fields":[
{
"type":"text",
"label":"First Name",
"name":"fname"
"value":"Abraham"
}
]
}
I'm able to load the HTML page from the above JSON schema, but when user clicks the submit button there will a new schema loaded through ajax call. Now if I pass the new schema to my reducers can I directly replace the props with the newSchema, as I heard mutating the props is not adviceable. In such case how do I update my props only with new data.
If I do it as follows in my reducer
return Object.assign(...formSchema, action.formSchema);
I see in console the next state is shown as object instead of an array even though I load the same set of data.
You can use componentWillReceiveProps() method in React to update your view with new props.
componentWillReceiveProps() is invoked before a mounted component receives new
props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
React doesn't call componentWillReceiveProps with initial props during mounting. It only calls this method if some of component's props may update. Calling this.setState generally doesn't trigger componentWillReceiveProps.
It looks like this:
class YourComponent extends Component{
constructor(props) {
this.state = {
message: props.message // initial prop
}
}
componentWillReceiveProps(nextProps){
this.setState({message: nextProps.message} // updated prop
}
render() {
return (
<div><h1>{this.state.message}</h1></div>
)
}
}
Note: In this code you're not updating/mutating props, instead you're using new props (nextProps) and updating the state of your view.

React Native setState

I am trying to change my state from the render function but it does not work.
render() {
return (
<View>
// some code
{this._renderModal()}
</View>
)
}
_renderModal() {
let channel = this.props.pusher.subscribe('my-channel');
channel.bind('my-event', data => {
this.setState({
requestedToEnter: false
});
this.props.navigation.getNavigator('root').push(Router.getRoute('order', { restaurant_id }));
});
return (
<Modal
animationType={'slide'}
transparent={false}
visible={this.state.requestedToEnter}
onRequestClose={this._handleEnter}>
// some components
</Modal>
)
}
When I receive the my-event on my-channel it fires the function that is bind to it. The setState method does not change the state (or it does but do not re-render the component). I think it's because I am using the setState method in the event function.. How I can close the Modal when I receive an event from pusher?
This is a bad practice to call this.setState function in your render() as each time setState is called, the component will render itself with the updated state values binding to your components / subcomponents.
Alternatively, you can create a state variable
this.state = {
isModalOpen: true
}
And put your pusher event listener in componentDidMount() and update your state variable value in the callback. Then, control your modal component visibility with the state variable:
<Modal
animationType={'slide'}
transparent={false}
visible={this.state.isModalOpen}></Modal>

Resources