you have to use componentDidUpdate in class components and useEffect in Function components...
componentDidUpdate like this:
componentDidUpdate(prevProps, prevState) {
if (prevProps.something !== this.props.something) {
console.log('something prop has changed.')
}
}
componentDidUpdate will run console.log every time the new prop is different from prevProps.
and in useEffect you just add the props to its dependency:
useEffect(()=>{
console.log('something prop has changed.')
},[props.something]);
useEffect will call that function every time the value of that prop changes.
you have to use componentDidUpdate in class components and useEffect in Function components...
componentDidUpdate like this:
componentDidUpdate(prevProps, prevState) {
if (prevProps.something !== this.props.something) {
console.log('something prop has changed.')
}
}
componentDidUpdate will run console.log every time the new prop is different from prevProps.
and in useEffect you just add the props to its dependency:
useEffect(()=>{
console.log('something prop has changed.')
},[props.something]);
useEffect will call that function every time the value of that prop changes.
Not sure what your question has to do with react redux but here is your code not showing the behavior you describe (there is nothing wrong with the code you posted in your question). Please provide a minimum reproducible example of your problem.
class ChildComponent extends React.Component {
componentWillReceiveProps(nextProps) {
console.log(this.props.myList, '==', nextProps.myList); // Outputs ['a','b','c'] == ['a','b','c']
}
render() {
return (
<pre>
{JSON.stringify(this.props.myList, undefined, 2)}
</pre>
);
}
}
const ParentCompoenent = () => {
const [myList, setMyList] = React.useState([1, 2, 3]);
const changeList = React.useCallback(
() => setMyList((list) => list.concat(list.length + 1)),
[]
);
return (
<div>
<button onClick={changeList}>Change list</button>
<ChildComponent myList={myList} />
</div>
);
};
ReactDOM.render(
<ParentCompoenent />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You should avoid using componentWillReceiveProps and maybe make the component a functional component and use the useEffect hook or use did mount, did update and possibly will unmount for class components.
How to fetch data when a React component prop changes?
React Call method on *SPECIFIC* props change
How to update state when prop changes?
Take an action on props change in ReactJS
Videos
Constructor is not a right place to make API calls.
You need to use lifecycle events:
componentDidMountto run the initial fetch.componentDidUpdateto 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.
I have set up redux in my React application, and props are being changed accordingly when the redux state is being changed, but I want to update my Component State when prop changes. I know there was a lifecycle function called willRecieveProps(), but I think is now deprecated.
Any help would be useful
Thanks.
As of v16.2.0, componentWillReceiveProps is the right place to update state, based on prop changes and since you want to use both current state and previous state in render, you need to maintain, two different state variables as you are doing
However, when you update the state based on previous state, use functional setState method
Check this answer for more details
When to use functional setState
componentWillReceiveProps(nextProps) {
if (nextProps.amount !== this.state.current) {
this.setState(prevState => ({ previous: prevState.current, current: nextProps.amount }));
}
}
According to the latest RFC to React
State derived from props/state
The purpose of this pattern is to calculate some values derived from props for use during render.
Typically
componentWillReceivePropsis used for this, although if the calculation is fast enough it could just be done inrender.:
From v16.3.0 onwards, you would make use of
static getDerivedStateFromProps(nextProps, prevState) {
if (
!prevState ||
prevState.current !== nextProps.amount
) {
return {
previous: prevState.current,
current: nextProps.amount
};
}
}
I'd like to update this answer for anyone else who comes here from Google. As of v16.8.6, componentWillReceiveProps has been marked as legacy, and is not recommended to use. Instead you should use componentDidUpdate and update your state based on the new props and previous props/previous state.
componentDidUpdate(prevProps, prevState) {
if (this.props.someVal !== prevState.someVal) {
this.setState({ previous: prevState.someVal, current: this.props.someVal });
}
}
Obviously, whether you check the previous state or the previous props is up to your discretion/situation. You can implement componentDidUpdate with or without the parameters, but if you want prevState you must declare prevProps.
React Update Lifecycle
componentDidUpdate()
you can't watch both props on one function, you'll have to add one watcher for every variable you want to trigger an action on change. You can create one handler (One single function) to be performed inside each watcher, like this:
<script>
export default {
props : ['employeeIdNumber','attentanceDate'],
data() {
return {
employee_id : this.employeeIdNumber,
date : this.attentanceDate
}
}
methods: {}
watch: {
employeeIdNumber(newVal, oldVal){
this.handler('param')
},
attenanceDate(newVal, oldVal){
this.handler('param')
}
},
methods: {
handler(params) {
// do something here
}
}
</script>
<script>
export default {
props : ['employeeIdNumber','attentanceDate'],
data() {
return {
employee_id : this.employeeIdNumber,
date : this.attentanceDate
}
}
watch: {
employeeIdNumber(newVal, oldVal){
this.employee_id = newVal
}
}
</script>
You can you vue JS's watch property to observe the change in any property or data
Haven't used Meteor before, but if you want to do things in response to state/prop changes then componentDidUpdate() is the lifecycle method for it. E.g.
componentDidUpdate(prevProps) {
if (this.props.bar !== prevProps.bar) {
// bar prop has changed
alert("bar changed");
}
}
If you're going to use Tracker.autorun, then the best place to call that is in componentDidMount, because it's called only once after the component has been mounted. You only need to call the tracker function once since the tracker function will rerun whenever the reactive data sources that it depends on ever changes. In the tracker function is where you will call maybeShowAlert depending on the value of bar like so,
componentDidMount() {
Tracker.autorun(() => {
let bar = this.props.bar;
if (bar) {
this.maybeShowAlert();
}
}
}
tl;dr:
-
how can I run a function whenever any component prop changes?
-
or, how can I force a component to get destroyed and re-created whenever the props change?
Edit: solution is keyed block: https://www.reddit.com/r/sveltejs/comments/113crbp/question_performing_action_whenever_any_component/j8psd85/
I'm creating a flashcard component that tracks the amount of time the user takes to click a button.
https://svelte.dev/repl/4507d4ca412843d693b1a15073603d81?version=3.55.1
This is easy - I can store the time when the component was created and the time when the button was clicked.
<script>
import { createEventDispatcher } from 'svelte'
export let question
export let answer
const dispatch = createEventDispatcher()
let startTime = new Date()
let endTime = null
let revealed = false
function reveal() {
revealed = true
endTime = new Date()
}
function next() {
dispatch('next')
}
</script>
<h1>Q: {question}</h1>
{#if revealed}
<h1>A: {answer}</h1>
<p>It took you {endTime - startTime} seconds to answer the question</p>
<button on:click={next}>Next question</button>
{:else}
<button on:click={reveal}>Show answer</button>
{/if}
The issue is that this component is being used inside another component, which changes the question & answer props once the next button is clicked.
This forces me to manually reset the state startTime, endTime, revealed inside the function next()
function next() {
startTime = new Date()
endTime = null
revealed = false
dispatch('next')
}Not too bad.
The issue now is that the startTime used now is the time when the component got reset, and not when the props changed.
If the container components waits a few seconds before setting the new props, then that additional time will get counted in the time to answer the flashcard.
I effectively want a way to either
-
forcefully destroy & recreate the component whenever the props change
-
or, get notified whenever the props change so that I can start the timer.
Now for single props, subscribing to the change is simple, albeit ugly & ununtuitive
export let question $: startTime = question, new Date()
The issue now is that I want the startTime to be reset whenever any of the props change.
This seems like running through way too many hoops to accomplish a simple task - observe when the props have changed.
Is there a better way?
You can watch props to execute some code upon props changes:
new Vue({
el: '#app',
data: {
text: 'Hello'
},
components: {
'child' : {
template: `<p>{{ myprop }}</p>`,
props: ['myprop'],
watch: {
myprop: function(newVal, oldVal) { // watch it
console.log('Prop changed: ', newVal, ' | was: ', oldVal)
}
}
}
}
});
<script src="https://unpkg.com/vue@2/dist/vue.min.js"></script>
<div id="app">
<child :myprop="text"></child>
<button @click="text = 'Another text'">Change text</button>
</div>
Have you tried this ?
watch: {
myProp: {
// the callback will be called immediately after the start of the observation
immediate: true,
handler (val, oldVal) {
// do your stuff
}
}
}
https://v2.vuejs.org/v2/api/#watch