bastej
bastej

Reputation: 83

how to re-render React children component when prop that parent pass is changed(redux)

I am little confused with redux and react...

I have parent component(SingleList) that render children component(NutrientsTable). Parent pass to children productList piece of state then children in componentDidMount call action that update currentListNutrients and then I use that in children render() to show.

I tried declare calculateNutrients as just helper method in component and then assign to variable and then use it in render(). As a result, it work fine but i gonna to put all app actions in Action Creator so I need do it with redux.

Parent Comp SingleList

import { connect } from "react-redux";
import NutrientsTable from "../NutrientsTable";

class SingleList extends Component {

  render() {
    return (
      <div className="single-list">
        <NutrientsTable 
          productsList={this.props.list.productsList}
        />
      </div>
    );
  }
}

function mapStateToProps({ lists }, ownProps) {
  return {
    list: lists[ownProps.match.params.id]
  };
}

export default connect(
  mapStateToProps,
  {}
)(SingleList);

Children Comp NutrientsTable

import { connect } from 'react-redux';
import { calculateNutrients } from '../actions';

class NutrientsTable extends Component {

    componentDidMount() {
        this.props.calculateNutrients(this.props.productsList);
    }

    render() { 
        const { calories, carbohydrates, proteins, fats } = this.props.nutrients;

        return (
            <div>{calories} {carbohydrates} {proteins} {fats}</div>
        )
    }
}

const mapStateToProps = ({currentListNutrients}) => {
    return { nutrients: currentListNutrients }
}

export default connect(mapStateToProps, { calculateNutrients })(NutrientsTable);

Action calculateNutrients

export function calculateNutrients(productsList) {
  let calories = 0,
    carbohydrates = 0,
    proteins = 0,
    fats = 0;
  _.map(productsList, product => {
    product.calories && (calories += product.calories * product.count);
    product.carbohydrates && (carbohydrates += product.carbohydrates * product.count);
    product.proteins && (proteins += product.proteins * product.count);
    product.fats && (fats += product.fats * product.count);
  });

  return {
    type: CALCULATE_NUTRIENTS,
    payload: {calories, carbohydrates, proteins, fats}
  }
}

And Reducer just return action.payload

Everything is ok when first render, but when I do some action in parent and change productList piece of state then children doesn't re-render with new productList. I know is it cause componentDidMount call just one. But where i should call action? I cant resolve it with any lifecycle method. Any suggestion?

Upvotes: 0

Views: 193

Answers (1)

Pho Huynh
Pho Huynh

Reputation: 1577

First, if you're using Redux, you don't have to pass down data from parent to children if data exists on Redux store (application's state). Just use connect for the children components. The children components should update when detecting any changes in Redux store (application's state).

Second, when you want the changes to happen, you have to dispatch and action to Redux which tells Redux to call api (or something like that) and update its store.

To handle the api calls, you should use Redux-thunk or Redux-saga.

Upvotes: 1

Related Questions