I have a react-router app that mounts User on /users. It works fine.
I then click a /users?page=2 link that passes in new props. Here getData uses this.props.location.query. So calling getData from componentWillReceiveProps will fetch stuff from page 1 instead of page 2.
I would have used componentHasReceivedProps method if it existed. What can I actually do?
componentWillMount: function(){
this.setState({data:null});
this.getData();
},
componentWillReceiveProps: function(nextProps){
this.setState({data:null});
this.getData();
},
Couple things.
1) Using this.setState synchronously inside componentWillMount works, but serves no purpose since you might as well have just done this inside of getInitialState instead.
2) If you want to react to props changes like this, you need to use the nextProps object inside componentWillreceiveProps. A simple fix is to change your getData method to allow the passing of nextProps to it. Such as:
getData: function(nextProps) {
var props = nextProps || this.props;
// your getData stuff
}
And then pass it in your componentWillReceiveProps method as this.getData(nextProps).
Related
I have a component that change some of his props when we inject different props.
I'm struggling to find a simple way to acces the state of my shallowed component from my test
Here is the code :
describe('componentWillReceiveProps', () => {
it('update state isDedicatedDealPriceSelected to true', () => {
const productComponent = shallow(<Product selectedPriceOption="Test" />);
productComponent.setProps({ selectedPriceOption: 'dedicatedDealPrice' });
expect(productComponent.props.isDedicatedDealPriceSelected).toBeTruthy();
});
});
I got undefined, i want to access the props isDedicatedDealPriceSelected that should be truthy. I think i'm miswritting something here on the last line in productComponent.props.isDedicatedDealPriceSelected
How can i access the props of my component ?
I'm using enzime to shallow render my component in the test with jest.
Thankd in advance !
EDIT : i was not looking to access the props, but instead the state ! sorry for the mispelling
It seems setProps takes a callback executed after the re-render. Maybe your test needs to be async and the assertion to be done inside this callback.
From the examples, only componentWillReceiveProps seems to be called synchronously.
https://airbnb.io/enzyme/docs/api/ShallowWrapper/setProps.html
To access the state of a shallow rendered component, you can access it using :
const wrapper = shallow(<component />);
wrapper.state().componentVar
To access a function of a shallow rendered component, you can access it using :
wrapper.instance().componentFunction()
In my project I have a call to an action that makes a webservice call and in turn dispatch actions to the result of the ws, these actions edit the store.
My problem is in :
ComponentDidUpdate () {
If (this.props.messages.length) {
Const items = this.props.messages.filter (this.isDisplayable);
This.timer = setInterval (() => {
If (items.length> 0) {
This.props.popItem (items);
} Else {
ClearInterval (this.timer);
}
}, This.props.interval);
}
}
In fact it is launched several times and I have warnings of
Warning: flattenChildren (...): Encountered two children with the same
key, 1. Child keys must be unique; When two children share a key,
only the first child will be used.
I used the componentDidMount but it launches it before api responds.
my question is:
Is that there is a way to update the component only at the response of my action, or alternatively to pass the warnings ?
try this :
componentWillReceiveProps(nextProps) {
if (this.props.messages === nextProps.messages) return;
i had some probleme and i resolve it by force update
forceUpdate () {
If (this.props.messages.length) {
...
}
}
In my project I have a call to an action that makes a webservice call and in turn dispatch actions to the result of the ws, these actions edit the store.
None of the methods componentDidMount and componentDidUpdate are good.
Observe the Store in Redux and update your component accordingly when the correct action TYPE is found.
Since you are using the Redux architecture, the state for all your components is in a single place — in the Store.
yes i know, but the problem is that componentDidUpdate is called several times which gives me the index error.
This is quite normal in React. Check this lifecycle.
What you should do is the govern the Redux architecture.
I will try today to provide some diagrams for you.
In general, anything you do will be from the global Store.
You may forget the React.Component state, and props you had in the non-Redux applications.
You typically need to use the Wrapper as a context provider around your app, where the context is the property of React.Component.
The context will be passed to all children and grandchildren so this will be the global Store organization.
Then you will need to read the Store from the context, and call the two typical methods: dispatch and subscribe.
I'm trying to update a child component as soon as it recieves new props. However, componentWillReceiveProps() in my child component is called before the props have actually updated. After reading this article i do understand why but it doesn't explain me how to solve my problem.
How do i call componentWillReceiveProps() after the props have updated?
Right now i'm cheating my way around it by letting a timeout run which waits for the actual update, but i really don't like this solution.
componentWillReceiveProps(){
var timeOut = setTimeout(() => this.loadPosts(), 100)
},
Thanks id advance!
Is it necessary to call componentWillReceiveProps after the props have updated? Or can you use the nextProps argument?
Eg. if you rewrite as:
componentWillReceiveProps(nextProps){
this.loadPosts(nextProps)
},
and then of course also rewrite the signature of loadPosts to allow manually passing in props:
loadPosts(props = this.props){
// do something with props
...
}
Use componentDidUpdate(prevProps, prevState). When it's called, two arguments are passed: prevProps and prevState. This is the inverse of componentWillUpdate. The passed values are what the values were, and this.props and this.state are the current values.
My TranslationDetail component is passed an id upon opening, and based on this an external api call is triggered in the class constructor, receiving data to the state, and this data being displayed on TranslationDetail.
//Routing:
<Route path="/translation/:id" component={TranslationDetail}/>
//Class:
class TranslationDetail extends Component {
constructor(props){
super(props);
this.props.fetchTrans(this.props.params.id);
}
This all works fine if I enter the url manually. In case I'd like to use react-router e.g. for displaying the next item like below the url does change, but the api call is not triggered, and the data will remain the same.
<button
type="button"
onClick={() =>
browserHistory.push(`/translation/${Number(this.props.params.id)+1}`)}>
Next
</button>
Please bear in mind that I'm a total beginner. The reason why this is happening is I believe that the constructor is run only once, thus no further api call is triggered.
How can I solve this?
Do I need to listed to props and call a function on change? If yes, how?
Constructor is not a right place to make API calls.
You need to use lifecycle events:
componentDidMount to run the initial fetch.
componentDidUpdate to make the subsequent calls.
Make sure to compare the props with the previous props in componentDidUpdate to avoid fetching if the specific prop you care about hasn't changed.
class TranslationDetail extends Component {
componentDidMount() {
this.fetchTrans();
}
componentDidUpdate(prevProps) {
if (prevProps.params.id !== this.props.params.id) {
this.fetchTrans();
}
}
fetchTrans() {
this.props.fetchTrans(this.props.params.id);
}
}
From React 16.3 and onwards componentWillMount, componentWillUpdate and componentWillReceiveProps are deprecated.
You can use static getDerivedStateFromProps and return a new state based on changes on props.
You don't have access to your this objects like props, so you cannot compare nextProps with your current props by nextProps.sth !== this.props.sth. You can compare you prevState value with nextProps and return new value of state.
Make sue you add UNSAFE_ to your current componentWillMount and the other deprecated lifecyle methods for now.
Use componentWillMount to get the data and set the state.
Then use componentWillReceiveProps for capturing update on the props.
You can check the Component Specs and Lifecycle.
I would use the render method. If the data is not loaded I would render a loader spinner and throw the action that fetch de data. For that i usually use the stores. Once the store has de data from the api, mark the data as loaded, throw an event and let the component get the data from the store, replacing the loader spinner with your data representation.
I have a situation that I'm sure is common and I just haven't learned the react way for accomplishing it. Let's say I have this:
var appView = new React.createClass({
render: function() {
return (
<div>
<SomeSubview/>
</div>
)
}
});
React.render(
React.createElement(appView),
$('#app').get(0)
);
My question is how should I create the SomeSubView react component so that it can render properly without any data, and then later render showing some data when the data is available. I have pub/sub system set up, so I'd like to be able to subscribe to an event and get the data to SomeSubView that way. SomeSubView might look something like this:
SomeSubView = new React.createClass({
componentDidMount: function() {
pubsub.subscribe({
callback: function() {
// something the sets the state or a prop of this component
}
});
},
render: function() {
// something that renders properly when
// there is no data and renders the data when there is data
return (
<div></div>
)
}
});
I can't tell if this a case for state or props on the react component? I don't know if it's best practice to put conditionals in the render function?
In your SomeSubView just check if data is available in your render function, but before returning the markup.
Like this:
SomeSubView = new React.createClass({
componentDidMount: function() {
pubsub.subscribe({
callback: function() {
// something the sets the state or a prop of this component
}
});
},
render: function() {
// something that renders properly when
if( this.state.data.length > 0 ){
var data = <li>{this.state.data}</li>;
}
return (
<div>{data}</div>
)
}
});
If the variable data is not set, React will simply pass over it as non-existent.
You can of course also use .map() on your state data to loop out markup just like in most render examples.
You have to use state like user3728205 said, especifically setState().
setState(function|object nextState[, function callback])
Merges nextState with the current state. This is the primary method
you use to trigger UI updates from event handlers and server request
callbacks.
The first argument can be an object (containing zero or more keys to
update) or a function (of state and props) that returns an object
containing keys to update.
Here is the simple object usage...
setState({mykey: 'my new value'});
What this says is that "whenever" you update your state via setState, React will execute the method render again for you. So, you should put yor display logic based on the state, when it changes the view displayed will change too.
I say "whenever" because React doesn't fire re-render immediatily, but creates a pending state transition.
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState
and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
For more information about the magic of React you should read this.
https://facebook.github.io/react/docs/reconciliation.html
A simple example that maybe can help.
And i recommend read the flux architecture that is very easy to understand and implement (is about utilizing a unidirectional data flow), and you have implementations like Fluxxor that facilitates the use of flux. This is for your pubsub part.