Skip to content

Avoid common pitfalls when using OnPush change detection in Angular

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

By default, Angular provides two different change detection strategies: Default and OnPush. Each has his own advantages, but sometimes we run into pitfalls if we don't understand how to apply OnPush and how it works. First, we need to understand how Angular implements change detection.

How Angular implements change detection?

Like any other framework, Angular must have a way to synchronize its internal model state to the view.

In particular, the framework by Google checks all components for changes in different occasions. These can include:

  • Network Requests
  • Mouse clicks
  • Mouse scrolls
  • Keyboard events, and more...

In general, all componentes are checked. The Angular team spent a lot of effort on highly optimizing the change check internally.

By default, it walks the application component's tree, an array, from start to finish.

On its way, it searches for children and values that have to be compared and checked (e.g., @Inputs(), @Outputs(), and other template-bound references).

But this comes with a downside. Angular cannot know which of the values is bound at some point, but us as developers, we can know when values change, and what value in an object changes, and this is where OnPush change detection comes in.

OnPush change detection

@Component({
    selector: 'app-this-dot',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: ...
})
export class ThisDotComponent {
    ...
}

When the OnPush change detection is declared as we see above, the change detection doesn't run automatically anymore. Instead, it listens for specific changes, and only runs the change detection in those scenarios. It runs if an @Input changes, or if a component was marked for a check. Remember, when comparing @Inputs, they are compared by identities (Object.is()).

OnPush and Object mutability

For ex. we declare a parent component and a child component.

// Parent component

@Component({
    selector: 'app-movie',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: `
      <div>
        <app-director [director]="movie.director"></app-movie>

        <button (click)="changeDirector()">Change Director</button>
      </div>
    `
})
export class MovieComponent {
    ...
    changeDirector() {
        this.director.name = 'Quentin';
    }

}

// Child component

@Component({
    selector: 'app-director',
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: ...
})
export class DirectorComponent {
    @Input() director: Director;
}

If we click on changeDirector() button, we will change the name of the movie director, as we set previously the changeDetection, this will not update the director on the template.

on-push-mutability

This occurs due to the fact:

  • we mutated the object directly
  • onPush works by comparing references of the inputs of the component
  • because we mutated an existing one, onPush change detector will not be triggered

BUT, we can avoid this situation, creating a new instance of the director instead of mutate the existing one.

...
changeDirector() {
    this.director = {
        name = 'Quentin',
        lastName = 'Tarantino'
    }
}
...

We simply need to either avoid mutating objects directly or use an immutability library to freeze the view model data that we pass to our components.

Let's see in action on Stackblitz.

OnPush and event handlers

If for example, we have an @Output event inside the director component, we will see that the director will show the new name and lastName information, but why is this?

This occurs because the triggering of event handlers cause the onPush change detector to trigger, independent of whether the inputs have changed or not.

We can then assume that OnPush is more than just checking input.

Conclusion

OnPush is defined this way.

It runs only in certain scenarios, and we need to keep that scenarios in mind if we want our app works smoothly. Those scenarios include:

  • When a DOM event the component listens was received
  • When the | async pipe receives a new event
  • When a @Input() was updated by change detection
  • When explicitely registering the component to be checked using ChangeDetectorRef

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Exploring Angular Forms: A New Alternative with Signals cover image

Exploring Angular Forms: A New Alternative with Signals

Exploring Angular Forms: A New Alternative with Signals In the world of Angular, forms are essential for user interaction, whether you're crafting a simple login page or a more complex user profile interface. Angular traditionally offers two primary approaches: template-driven forms and reactive forms. In my previous series on Angular Reactive Forms, I explored how to harness reactive forms' power to manage complex logic, create dynamic forms, and build custom form controls. A new tool for managing reactivity - signals - has been introduced in version 16 of Angular and has been the focus of Angular maintainers ever since, becoming stable with version 17. Signals allow you to handle state changes declaratively, offering an exciting alternative that combines the simplicity of template-driven forms with the robust reactivity of reactive forms. This article will examine how signals can add reactivity to both simple and complex forms in Angular. Recap: Angular Forms Approaches Before diving into the topic of enhancing template-driven forms with signals, let’s quickly recap Angular's traditional forms approaches: 1. Template-Driven Forms: Defined directly in the HTML template using directives like ngModel, these forms are easy to set up and are ideal for simple forms. However, they may not provide the fine-grained control required for more complex scenarios. Here's a minimal example of a template-driven form: ` ` 2. Reactive Forms: Managed programmatically in the component class using Angular's FormGroup, FormControl, and FormArray classes; reactive forms offer granular control over form state and validation. This approach is well-suited for complex forms, as my previous articles on Angular Reactive Forms discussed. And here's a minimal example of a reactive form: ` ` Introducing Signals as a New Way to Handle Form Reactivity With the release of Angular 16, signals have emerged as a new way to manage reactivity. Signals provide a declarative approach to state management, making your code more predictable and easier to understand. When applied to forms, signals can enhance the simplicity of template-driven forms while offering the reactivity and control typically associated with reactive forms. Let’s explore how signals can be used in both simple and complex form scenarios. Example 1: A Simple Template-Driven Form with Signals Consider a basic login form. Typically, this would be implemented using template-driven forms like this: ` ` This approach works well for simple forms, but by introducing signals, we can keep the simplicity while adding reactive capabilities: ` ` In this example, the form fields are defined as signals, allowing for reactive updates whenever the form state changes. The formValue signal provides a computed value that reflects the current state of the form. This approach offers a more declarative way to manage form state and reactivity, combining the simplicity of template-driven forms with the power of signals. You may be tempted to define the form directly as an object inside a signal. While such an approach may seem more concise, typing into the individual fields does not dispatch reactivity updates, which is usually a deal breaker. Here’s an example StackBlitz with a component suffering from such an issue: Therefore, if you'd like to react to changes in the form fields, it's better to define each field as a separate signal. By defining each form field as a separate signal, you ensure that changes to individual fields trigger reactivity updates correctly. Example 2: A Complex Form with Signals You may see little benefit in using signals for simple forms like the login form above, but they truly shine when handling more complex forms. Let's explore a more intricate scenario - a user profile form that includes fields like firstName, lastName, email, phoneNumbers, and address. The phoneNumbers field is dynamic, allowing users to add or remove phone numbers as needed. Here's how this form might be defined using signals: ` > Notice that the phoneNumbers field is defined as a signal of an array of signals. This structure allows us to track changes to individual phone numbers and update the form state reactively. The addPhoneNumber and removePhoneNumber methods update the phoneNumbers signal array, triggering reactivity updates in the form. ` > In the template, we use the phoneNumbers signal array to dynamically render the phone number input fields. The addPhoneNumber and removePhoneNumber methods allow users to reactively add or remove phone numbers, updating the form state. Notice the usage of the track function, which is necessary to ensure that the ngFor directive tracks changes to the phoneNumbers array correctly. Here's a StackBlitz demo of the complex form example for you to play around with: Validating Forms with Signals Validation is critical to any form, ensuring that user input meets the required criteria before submission. With signals, validation can be handled in a reactive and declarative manner. In the complex form example above, we've implemented a computed signal called formValid, which checks whether all fields meet specific validation criteria. The validation logic can easily be customized to accommodate different rules, such as checking for valid email formats or ensuring that all required fields are filled out. Using signals for validation allows you to create more maintainable and testable code, as the validation rules are clearly defined and react automatically to changes in form fields. It can even be abstracted into a separate utility to make it reusable across different forms. In the complex form example, the formValid signal ensures that all required fields are filled and validates the email and phone numbers format. This approach to validation is a bit simple and needs to be better connected to the actual form fields. While it will work for many use cases, in some cases, you might want to wait until explicit "signal forms" support is added to Angular. Tim Deschryver started implementing some abstractions around signal forms, including validation and wrote an article about it. Let's see if something like this will be added to Angular in the future. Why Use Signals in Angular Forms? The adoption of signals in Angular provides a powerful new way to manage form state and reactivity. Signals offer a flexible, declarative approach that can simplify complex form handling by combining the strengths of template-driven forms and reactive forms. Here are some key benefits of using signals in Angular forms: 1. Declarative State Management: Signals allow you to define form fields and computed values declaratively, making your code more predictable and easier to understand. 2. Reactivity: Signals provide reactive updates to form fields, ensuring that changes to the form state trigger reactivity updates automatically. 3. Granular Control: Signals allow you to define form fields at a granular level, enabling fine-grained control over form state and validation. 4. Dynamic Forms: Signals can be used to create dynamic forms with fields that can be added or removed dynamically, providing a flexible way to handle complex form scenarios. 5. Simplicity: Signals can offer a simpler, more concise way to manage form states than traditional reactive forms, making building and maintaining complex forms easier. Conclusion In my previous articles, we explored the powerful features of Angular reactive forms, from dynamic form construction to custom form controls. With the introduction of signals, Angular developers have a new tool that merges the simplicity of template-driven forms with the reactivity of reactive forms. While many use cases warrant Reactive Forms, signals provide a fresh, powerful alternative for managing form state in Angular applications requiring a more straightforward, declarative approach. As Angular continues to evolve, experimenting with these new features will help you build more maintainable, performant applications. Happy coding!...

A Deep Dive into SvelteKit's Rendering Techniques cover image

A Deep Dive into SvelteKit's Rendering Techniques

A Deep Dive into SvelteKit's Rendering Techniques Introduction SvelteKit is a meta-framework for Svelte that allows you to develop pages based on their content. At its core, SvelteKit introduces three fundamental strategies out of the box, each designed to streamline the development process and adapt to the specific needs of your project. These strategies enable you to easily create dynamic, responsive, and highly interactive web applications. These strategies, or as SvelteKit calls them, page options, are: * Prerender: Ideal for static content that doesn't change, making your pages lightning-fast to load. * SSR (Server-Side Rendering): Ideal for rendering full pages with dynamic content from a server. * CSR (Client-Side Rendering): Best for highly dynamic and interactive applications where content updates frequently based on user actions. These page options can be applied to specific pages (when exported from **+page.js or +page.server.js) or to a group of pages (when exported from +layout.js or +layout.server.js). They can also be set across the entire application. You accomplish this by exporting it from the root layout. It's worth noting that child layouts and pages can supersede settings from parent layouts. This means you could activate prerendering for the whole app and then turn it off for certain pages that require dynamic server rendering (SSR). In this blog post, we'll explore these page options and share some insights on how you might use them in everyday web projects. Prerendering Prerendering is akin to taking a snapshot of your web pages when you build your application; you might have heard of this as static rendering. This snapshot is then served to all users, ensuring lightning-fast load times since the server simply delivers the pre-built files without additional processing rather than dynamically generating the files for each request. This method is perfect for content that remains unchanged across visits, such as blog posts, documentation, or landing pages. In other words, pages with no dynamic content. There will be situations where you would like to avoid using this option, though. The rule of thumb is that if two different users will see different content, then this is a no-go. Other reasons are inconvenience for your needs. For example, your build times could drag if you end up prerendering tons of pages. Is that convenient to you? Well, that’s for you to decide! The prerender option is turned off by default, so we need to enable it if we want to start using it. Export the following in the +page.server.js or just +page.js: ` Likewise, if you have a group of pages, you could do the same inside a +layout.js or +layout.server.js . If your app is suitable to be all SSG (Static Side Generation), you could use adapter-static, which will output files suitable for use with any static web server. In cases where you happen to opt-in for this, you could turn off pages that need dynamic rendering: ` Another cool feature about prerendering is that pages that fetch data from server routes can automatically inherit default values during the prerendering process. This feature simplifies data management and enhances the development workflow, especially when dealing with dynamic content that needs to be prerendered with specific data sets. Let's say you have a blog where each post fetches its content from a server route when the page loads. Normally, this would require the user's browser to make a request to the server at runtime. However, with prerendering, you can have this data fetched and embedded into the page at build time. ` In this example, when the blog/[slug].sveltepage is prerendered, it makes a fetch call to /api/posts/[slug], which returns the post data. This data is then used to prerender the blog post page with the content already in place, allowing the page to load instantly for the user, with the blog post content visible even before any JavaScript is executed on the client side. There’s a third and useful option when prerendering: ` Using this option is like telling SvelteKit to make a smart guess about whether to prerender your page in advance. You're saying, "Hey SvelteKit, you decide if this page should be prerendered based on what you find about the page" SvelteKit analyzes your page and if it determines the page can be prerendered without complications, it will automatically generate a prerendered version. This process involves SvelteKit either crawling a link inside an already prerendered page, prerendering entry points or targeting pages that are specifically marked for prerendering set in entries. An example is a blog section where you post articles regularly. The blog section has a main page that lists all your blog posts and individual pages for each blog post. The structure for this might be something like: In this setup, the /blog main entry page will be prerendered automatically. However, individual blog posts at paths like /blog/[slug] will not be prerendered unless linked directly from another prerendered page. For instance, if there’s a link to /blog/some-cool-slug, like: &lt;a href="/blog/some-cool-slug">, SvelteKit will be able to crawl that link and prerender that specific page as well. Now let’s say that you would like to prerender your latest blog post, but there's no link for SvelteKit to crawl to this page. In these cases, you can explicitly add these pages to the prerender entries list. By doing so, SvelteKit will prerender every specified entry, ensuring that the content is immediately accessible to users. You can configure the prerender entries directly in your svelte.config.js file or by exporting an entries function from a +page.js, a +page.server.js or a +server.js belonging to a dynamic route. Here’s how you can do it: ` SSR SSR is similar to prerendering. It ensures that pages are first rendered on the server. This process generates the complete HTML content, which is then sent to the client for hydration). This approach enhances the initial page load performance and SEO, providing a better user experience. Unlike prerendering, where pages are generated at build time, SSR pages are created at runtime. This key difference allows for the generation of dynamic content, making SSR particularly suited for applications that require personalized content for each user or real-time updates, such as user dashboards, e-commerce sites, and social media platforms. Similar to prerendering, SSR comes with its own set of limitations, and there are scenarios where it might not be the best choice for your project: * SSR can be expensive. It can significantly load your server, especially for high-traffic sites, as each page request involves rendering content on the server. If server resources are constrained, this could lead to performance bottlenecks. * Highly Interactive Applications: Applications that rely heavily on user interactions and real-time updates (like games or interactive tools like an admin panel) might not benefit much from SSR. CSR can provide a smoother user experience in these cases, as it minimizes server requests after the initial load. * SEO Is Not YOUR Priority: If search engine optimization isn't a key concern for your project (for example, an internal dashboard or an app behind a login), the SEO benefits of SSR might not justify the additional complexity and server demands. export const ssr = true is the option enabled by default. Thus, we can use its benefits from the start. To execute code exclusively on the server, such as fetching data from a database or an external API, SvelteKit offers a convention using +page.server.js files. This setup is ideal for server-side operations, ensuring sensitive logic and credentials are not exposed to the client. ` Note: When you employ +page.js in SvelteKit, the contained code is executed on both the server and client sides by default. However, if you intend for the code to run exclusively on the client side, you can disable SSR by setting export const ssr = false within your +page.js file. Doing so ensures that the code is only executed in the browser, adhering to client-side rendering principles and turning your app into a SPA. This is useful for cases where you don’t need the load on the server, or you don’t benefit from any of the benefits of SSR. CSR CSR plays a crucial role in making web pages interactive by hydrating them and incorporating JavaScript. JavaScript is crucial for adding interactivity to web pages—everything from animations and video players to form validations and dynamic content updates relies on JavaScript. However, there are instances where JavaScript is unnecessary. Consider a prerendered 'About' page; enabling CSR on this page could be excessive, especially if it lacks interactive elements. In such scenarios, you can streamline your page by disabling CSR that comes enabled by default, which can be done by simply doing this: ` Using this trick smartly can make your web pages load fast because downloading JavaScript is sometimes heavy. The key lies in strategically combining this approach with other rendering techniques to optimize performance and user experience. One factor to consider, though, is that turning off CSR also means you'll lose client-side routing. This requires you to depend on traditional browser navigation instead. Conclusion By thoughtfully applying these strategies, you can craft a SvelteKit application that performs exceptionally and delivers a fantastic user experience tailored to your audience's needs. Wrapping up our journey through SSR with SvelteKit. We discovered how choosing the right way to render pages (like prerendering, SSR, or CSR) is super important and can make a big difference in how well a website works. SvelteKit is awesome because it lets you pick the best method for your website. So, remember, playing with SvelteKit and these rendering methods can make your websites stand out. Use it to build websites that not only work great but are also fun and easy for people to use. Dive in, try things out, and see how your web projects can shine!...

What's New in React 18? cover image

What's New in React 18?

Finally, we have a new version of React. Officially, React 18 is now ready to use! What new features were introduced in this version? Let’s find out in detail. In my opinion, the latest feature that revolutionized the React Ecosystem was React Hooks, introduced in React 16.8 back in 2019. Since then, we have seen many versions being released. But without any major changes, what will happen in React 18? Let’s dig into the details. New Features Concurrent React According to the official website of React, this is the major addition in React 18. With this new feature released, the devs will benefit from being able to prepare multiple versions of your UI at the same time. So, React will delegate to the dev to identify which changes are important to re-render the component. ` ` Automatic Batching With this new exciting feature, React will combine multiple setState() calls into a single re-render to improve performance. Before this, if we had multiple setState() inside of a setTimeout(), React will re-render for every state update which sometimes causes performance issues if we have larger components. You may already be familiar with this. This feature was available in React 17, but now it comes out-of-the-box. ` ` Transitions This new feature lets the dev split the state update into two categories: urgent and low priority. For some actions, like selecting in a dropdown or updating a input, you want the action to respond immediately, so now we had the possibility to distinguish between an urgent update and a non-urgent one. Updates wrapped in startTransition are handled as non-urgent, and will be interrupted if more urgent updates like clicks or keypresses come in. ` New Suspense Features Finally, we will have Suspense in the server. In the apps that are server-rendered, we will have only one Suspense API. Another cool thing is if we use Suspense in the server, we will start progressively streaming HTML. This enables selective hydration in the client. On paper, it looks awesome, but what does that mean? If you are waiting for some data on the server, React can stream HTML for the fallback, and let the user see the rest of the page. When the data is ready, the user will able to see the content HTML. This means that a single slow data source on the server will no longer hold the entire page back. New Hooks When we started this post, we mentioned that a few years ago, Hooks were the latest feature release in React. Now, React 18 will bring new Hooks: - useId : we can use this new hook to create a new identifier in the server and in the client. ` - useTransition: to create a new state update that has lower priority. ` - useDeferredValue: to update some deferred values in a controlled way (perfect for inputs, selects, etc). ` - useSyncExternalStore - useInsertionEffect IE11 not longer supported IE11 is no longer supported since React 18. If your app needs to continue bringing support to IE11, you will need to stay at React 17.x. Conclusion The most recent release from React includes some cool features like Automatic Batching or Concurrent Mode, which should improve the performance of React in the latest release. I'm really excited to see how these new features will perform in a real-life app, but they look more than promising. As we see, React 18 introduced out-of-the-box improvements, and new features that look incredible. It has cleared the way for new possibilities in React.js app development. If you want be aware of the upcoming or most recents changes, you can check out React JS Meetups....

The Quirks And Gotchas of PHP cover image

The Quirks And Gotchas of PHP

The Quirks And Gotchas of PHP If you come from a JavaScript background, you'll likely be familiar with some of its famous quirks, such as 1 + "1" equaling "11". Well, PHP has its own set of quirks and gotchas, too. Some are oddly similar to JavaScript's, while others can surprise a JavaScript developer. Let's start with the more familiar ones. 1. Type Juggling and Loose Comparisons Like JavaScript, PHP has two types of comparison operators: strict and loose. The loose comparison operator in PHP uses ==, while the strict comparison operator uses ===. Here's an example of a loose vs. strict comparison in PHP: ` PHP is a loosely typed language, meaning it will automatically convert variables from one type to another when necessary, just like JavaScript. This is not only when doing comparisons but also, for example, when doing numeric operations. Such conversions can lead to some unexpected results if you're not careful: ` As you can see, the type system has gotten a bit stricter in PHP 8, so it won't let you commit some of the "atrocities" that were possible in earlier versions, throwing a TypeError instead. PHP 8 introduced many changes that aim to eliminate some of the unpredictable behavior; we will cover some of them throughout this article. 1.1. Truthiness of Strings This is such a common gotcha in PHP that it deserves its own heading. By default, PHP considers an empty string as false and a non-empty string as true: ` But wait, there's more! PHP also considers the string "0" as false: ` You might think we're done here, but no! Try comparing a string such as "php" to 0: ` Until PHP7, any non-numeric string was converted to 0 when cast to an integer to compare it to the other integer. That's why this example will be evaluated as true. This quirk has been fixed in PHP 8. For a comprehensive comparison table of PHP's truthiness, check out the PHP documentation. 1.2. Switch Statements Switch statements in PHP use loose comparisons, so don't be surprised if you see some unexpected behavior when using them: ` The New Match Expression in PHP 8 PHP 8 introduced the match expression, which is similar to switch but uses strict comparisons (i.e., === under the hood) and returns a value: ` Unlike switch, there is no "fall-through" behavior in match, and each branch must return a value, making match a great alternative when you need a more precise or concise form of branching—especially if you want to avoid the loose comparisons of a traditional switch. 1.3 String to Number Conversion In earlier versions of PHP, string-to-number conversions were often done silently, even if the string wasn’t strictly numeric (like '123abc'). In PHP 7, this would typically result in 123 plus a Notice: ` In PHP 8, you’ll still get int(123), but now with a Warning, and in other scenarios (like extremely malformed strings), you might see a TypeError. This stricter behavior can reveal hidden bugs in code that relied on implicit type juggling. Stricter Type Checks & Warnings in PHP 8 - Performing arithmetic on non-numeric strings: As noted, in older versions, something like "123abc" + 0 would silently drop the non-numeric part, often producing 123 plus a PHP Notice. In PHP 8, such operations throw a more visible Warning or TypeError, depending on the exact scenario. - Null to Non-Nullable Internal Arguments: Passing null to a function parameter that’s internally declared as non-nullable will trigger a TypeError in PHP 8. Previously, this might have been silently accepted or triggered only a warning. - Internal Function Parameter Names: PHP 8 introduced named arguments but also made internal parameter names part of the public API. If you use named arguments with built-in functions, be aware that renaming or reordering parameters in future releases might break your code. Always match official parameter names as documented in the PHP manual. Union Types & Mixed Since PHP 8.0, we can declare union types, which allows you to specify that a parameter or return value can be one of multiple types. For example: ` Specifying the union of types your function accepts can help clarify your code’s intent and reveal incompatibilities if your existing code relies on looser type checking, preventing some of the conversion quirks we’ve discussed. 2. Operator Precedence and Associativity Operator precedence can lead to confusing situations if you’re not careful with parentheses. For instance, the . operator (string concatenation similar to + in JavaScript) has left-to-right associativity, but certain logical operators have lower precedence than assignment or concatenation, leading to puzzling results in PHP 7 and earlier: ` PHP 8 has fixed this issue by making the + and - operators take a higher precedence. 3. Variable Variables and Variable Functions Now, we're getting into unfamiliar territory as JavaScript Developers. PHP allows you to define variable variables and variable functions. This can be a powerful feature, but it can also lead to some confusing code: ` In this example, the variable $varName contains the string 'hello'. By using $$varName, we're creating a new variable with the name 'hello' and assigning it the value 'world'. Similarly, you can create variable functions: ` 4. Passing Variables by Reference You can pass variables by reference using the & operator in PHP. This means that any changes made to the variable inside the function will be reflected outside the function: ` While this example is straightforward, not knowing the pass-by-reference feature can lead to some confusion, and bugs can arise when you inadvertently pass variables by reference. 5. Array Handling PHP arrays are a bit different from JavaScript arrays. They can be used as both arrays and dictionaries, and they have some quirks that can catch you off guard. For example, if you try to access an element that doesn't exist in an array, PHP will return null instead of throwing an error: ` Furthermore, PHP arrays can contain both numerical and string keys at the same time, but numeric string keys can sometimes convert to integers, depending on the context> ` In this example: - "1" (string) and 1 (integer) collide, resulting in the array effectively having only one key: 1. - true is also cast to 1 as an integer, so it overwrites the same key. And last, but not least, let's go back to the topic of passing variables by reference. You can assign an array element by reference, which can feel quite unintuitive: ` 6 Checking for Variable Truthiness (isset, empty, and nullsafe operator) In PHP, you can use the empty() function to check if a variable is empty. But what does "empty" mean in PHP? The mental model of what's considered "empty" in PHP might differ from what you're used to in JavaScript. Let's clarify this: The following values are considered empty by the empty() function: - "" (an empty string) - 0 (0 as an integer) - 0.0 (0 as a float) - "0" (0 as a string) - null - false - [] (an empty array) This means that the following values are not considered empty: - "0" (a string containing "0") - " " (a string containing a space) - 0.0 (0 as a float) - new stdClass() (an empty object) Keep this in mind when using empty() in your code, otherwise, you might end up debugging some unexpected behavior. Undefined Variables and isset() Another little gotcha is that you might expect empty() to return true for undefined variables too - they contain nothing after all, right? Unfortunately, empty() will throw a notice in such case. To account for undefined variables, you may want to use the isset() function, which checks if a variable is set and not null: ` The Nullsafe Operator If you have a chain of properties or methods that you want to access, you may tend to check each step with isset() to avoid errors: ` In fact, because isset() is a special language construct and it doesn't fully evaluate an undefined part of the chain, it can be used to evaluate the whole chain at once: ` That's much nicer! However, it could be even more elegant with the nullsafe operator (?->) introduced in PHP 8: ` If you’ve used optional chaining in JavaScript or other languages, this should look familiar. It returns null if any part of the chain is null, which is handy but can also hide potential logic mistakes — if your application logic expects objects to exist, silently returning null may lead to subtle bugs. Conclusion While PHP shares a few loose typing quirks with JavaScript, it also has its own distinctive behaviors around type juggling, operator precedence, passing by reference, and array handling. Becoming familiar with these nuances — and with the newer, more predictable features in PHP 8 — will help you avoid subtle bugs and write clearer, more robust code. PHP continues to evolve, so always consult the official documentation to stay current on best practices and language changes....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co