Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).

Moving the Rep Logs to State

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Communication always flows down in React: data lives in one component and is passed down to its children as props. Actually, both data and callbacks are passed from parent to child: child components use callbacks to communicate back up to the parent when something happens. For example, RepLogs passes an onRowClick to RepLogList. It uses that to tell its parent when that interaction occurs.

So, parents pass data to their children. But, parents do not, ask children for information. Well, it's technically possible, but it's not the normal flow.

For example, RepLogApp passes the highlightedRowId to RepLogs. But, RepLogApp does not ever ask RepLogs to give it any data that lives inside RepLogs. Information only flows down.

Why state Lives up High

For that reason, in general, we need the state of our application to live as high up the component hierarchy as possible. Why? Because we can pass a piece of state down to all of the components that need to use it. When that state changes, that change naturally flows down and everything updates beautifully.

But, imagine if a piece of state lived in a child component, but we wanted to use it in the render() method of a parent. Well, that just won't work! The parent can't ask the child for that data: information does not flow up.

This is the reason why we will put all of our state in the top level component: RepLogApp. Again, this is not an absolute rule, but it's a great rule to follow for now. We'll talk later about when it's ok to move state lower, into a child component.

Moving RepLogs to state

Anyways, the most important piece of data in our app is the rep logs themselves. And, these will need to change dynamically as the user adds new rep logs and deletes old ones. That means, rep logs need to be stored as state.

To get the static version of our app up and running, we just hardcoded these inside RepLogList. Time to move this to state! Copy the dummy rep log data and go to RepLogApp. Whenever we have new state, we need to initialize it in the constructor. Add a new repLogs key to this array and paste!

... lines 1 - 5
constructor(props) {
... lines 7 - 8
this.state = {
highlightedRowId: null,
repLogs: [
{ id: 1, reps: 25, itemLabel: 'My Laptop', totalWeightLifted: 112.5 },
{ id: 2, reps: 10, itemLabel: 'Big Fat Cat', totalWeightLifted: 180 },
{ id: 8, reps: 4, itemLabel: 'Big Fat Cat', totalWeightLifted: 72 }
]
};
... lines 17 - 18
}
... lines 20 - 42

Yea, eventually the repLogs state will start empty, and we'll then populate it by making an AJAX call to the server for the existing rep logs. But, until then, the dummy data makes building things easier.

Passing the RepLogs State Down

The new state lives in the top-level component. But... we need to use it down in RepLogList. No problem! We just need to pass this down our tree. Fetch the repLogs out of state, then pass this as a prop to RepLogs.

... lines 1 - 24
render() {
... lines 26 - 28
return (
<RepLogs
... lines 31 - 33
repLogs={repLogs}
/>
)
}
... lines 38 - 42

In RepLogs, before using the new prop, head down to the bottom: we want to define all props in propTypes. Add repLogs set to PropTypes.array.isRequired.

... lines 1 - 79
RepLogs.propTypes = {
... lines 81 - 83
repLogs: PropTypes.array.isRequired
};

Copy that, because, RepLogList will receive the same prop.

... lines 1 - 24
RepLogList.propTypes = {
... lines 26 - 27
repLogs: PropTypes.array.isRequired,
};

Ok! We are passing the repLogs prop to the RepLogs component. At the top of render(), read repLogs out of props. And then, do the prop-passing dance: send this straight into RepLogList.

... lines 1 - 4
export default function RepLogs(props) {
const { withHeart, highlightedRowId, onRowClick, repLogs } = props;
... lines 7 - 12
return (
... lines 14 - 25
<RepLogList
... lines 27 - 28
repLogs={repLogs}
/>
... lines 31 - 76
);
}
... lines 79 - 86

Finally, in that component, get repLogs out of props and... delete the hardcoded stuff.

... lines 1 - 3
export default function RepLogList(props) {
const { highlightedRowId, onRowClick, repLogs } = props;
... lines 6 - 22
}
... lines 24 - 30

This is sweet! Move back to your browser and refresh! Hey hey! It's not broken! Check out the React dev tools, and look at the top RepLogApp component. Yep! You can see the repLogs state. Now... mess with it! Change the reps from 25 to 50.... boom! The UI on the child component updates instantly!

But, look back at RepLogApp, it has two pieces of state & one prop. And... it's passing all of that into its child as props. With a trick, we can be lazier, and do this automatically.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

While the concepts of this course are still largely applicable, it's built using an older version of Symfony (4) and React (16).

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^1.6", // 1.9.1
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.3
        "doctrine/doctrine-fixtures-bundle": "~3.0", // 3.0.2
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.1
        "doctrine/orm": "^2.5", // v2.7.2
        "friendsofsymfony/jsrouting-bundle": "^2.2", // 2.2.0
        "friendsofsymfony/user-bundle": "dev-master#4125505ba6eba82ddf944378a3d636081c06da0c", // dev-master
        "sensio/framework-extra-bundle": "^5.1", // v5.2.0
        "symfony/asset": "^4.0", // v4.1.4
        "symfony/console": "^4.0", // v4.1.4
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/form": "^4.0", // v4.1.4
        "symfony/framework-bundle": "^4.0", // v4.1.4
        "symfony/lts": "^4@dev", // dev-master
        "symfony/monolog-bundle": "^3.1", // v3.3.0
        "symfony/polyfill-apcu": "^1.0", // v1.9.0
        "symfony/serializer-pack": "^1.0", // v1.0.1
        "symfony/swiftmailer-bundle": "^3.1", // v3.2.3
        "symfony/twig-bundle": "^4.0", // v4.1.4
        "symfony/validator": "^4.0", // v4.1.4
        "symfony/yaml": "^4.0", // v4.1.4
        "twig/twig": "2.10.*" // v2.10.0
    },
    "require-dev": {
        "symfony/debug-pack": "^1.0", // v1.0.6
        "symfony/dotenv": "^4.0", // v4.1.4
        "symfony/maker-bundle": "^1.5", // v1.5.0
        "symfony/phpunit-bridge": "^4.0", // v4.1.4
        "symfony/web-server-bundle": "^4.0" // v4.1.4
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "dependencies": {
        "@babel/plugin-proposal-object-rest-spread": "^7.12.1" // 7.12.1
    },
    "devDependencies": {
        "@babel/preset-react": "^7.0.0", // 7.12.5
        "@symfony/webpack-encore": "^0.26.0", // 0.26.0
        "babel-plugin-transform-object-rest-spread": "^6.26.0", // 6.26.0
        "babel-plugin-transform-react-remove-prop-types": "^0.4.13", // 0.4.13
        "bootstrap": "3", // 3.3.7
        "copy-webpack-plugin": "^4.4.1", // 4.5.1
        "core-js": "2", // 1.2.7
        "eslint": "^4.19.1", // 4.19.1
        "eslint-plugin-react": "^7.8.2", // 7.8.2
        "font-awesome": "4", // 4.7.0
        "jquery": "^3.3.1", // 3.3.1
        "promise-polyfill": "^8.0.0", // 8.0.0
        "prop-types": "^15.6.1", // 15.6.1
        "react": "^16.3.2", // 16.4.0
        "react-dom": "^16.3.2", // 16.4.0
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^7.0.0", // 7.3.1
        "sweetalert2": "^7.11.0", // 7.22.0
        "uuid": "^3.2.1", // 3.4.0
        "webpack-notifier": "^1.5.1", // 1.6.0
        "whatwg-fetch": "^2.0.4" // 2.0.4
    }
}
userVoice