Preact: <select> elements aren't selecting a matching value after update

Created on 12 Dec 2016  路  9Comments  路  Source: preactjs/preact

preact version: 7.1.0

Update: I created a Codepen demo showing the behavior.

I have a component that renders three <select> elements representing a users Google Analytics account tree structure (accounts > properties > views). When the selected account is updated, the property and view <select>s should update accordingly, but in many cases an empty select is displayed. (Note the empty select is never displayed on the initial render, only after user updates).

Steps to reproduce:

1) Include the ViewSelector component code at the bottom of this issue in a Preact app.
2) Once the page loads, the ViewSelector renders with "Account A" selected
3) Select "Account C" then select "Account B"
4) Notice how once "Account B" is selected, the property and view <select>s aren't being populated.

Note: the code works in React as well as Preact-compat.

import {h, Component} from 'preact';

const accountSummaries = {
  "kind": "analytics#accountSummaries",
  "username": "[email protected]",
  "totalResults": 5,
  "startIndex": 1,
  "itemsPerPage": 1000,
  "items": [
    {
      "id": "1001",
      "kind": "analytics#accountSummary",
      "name": "Account A",
      "webProperties": [
        {
          "kind": "analytics#webPropertySummary",
          "id": "UA-1001-1",
          "name": "WebProperty A.A",
          "internalWebPropertyId": "3001",
          "level": "STANDARD",
          "websiteUrl": "http://example.com",
          "profiles": [
            {
              "kind": "analytics#profileSummary",
              "id": "2001",
              "name": "Profile A.A.A",
              "type": "WEB"
            },
            {
              "kind": "analytics#profileSummary",
              "id": "2002",
              "name": "Profile A.A.B",
              "type": "WEB"
            },
            {
              "kind": "analytics#profileSummary",
              "id": "2003",
              "name": "Profile A.A.C",
              "type": "WEB"
            }
          ]
        },
        {
          "kind": "analytics#webPropertySummary",
          "id": "UA-1001-2",
          "name": "WebProperty A.B",
          "internalWebPropertyId": "3002",
          "level": "STANDARD",
          "websiteUrl": "http://example.com",
          "profiles": [
            {
              "kind": "analytics#profileSummary",
              "id": "2004",
              "name": "Profile A.B.A",
              "type": "WEB"
            },
            {
              "kind": "analytics#profileSummary",
              "id": "2005",
              "name": "Profile A.B.B",
              "type": "WEB"
            },
            {
              "kind": "analytics#profileSummary",
              "id": "2006",
              "name": "Profile A.B.C",
              "type": "WEB"
            }
          ]
        },
        {
          "kind": "analytics#webPropertySummary",
          "id": "UA-1001-3",
          "name": "WebProperty A.C",
          "internalWebPropertyId": "3003",
          "level": "STANDARD",
          "websiteUrl": "http://example.com",
          "profiles": [
            {
              "kind": "analytics#profileSummary",
              "id": "2007",
              "name": "Profile A.C.A",
              "type": "WEB"
            },
            {
              "kind": "analytics#profileSummary",
              "id": "2008",
              "name": "Profile A.C.B",
              "type": "WEB"
            },
            {
              "kind": "analytics#profileSummary",
              "id": "2009",
              "name": "Profile A.C.C",
              "type": "WEB"
            }
          ]
        }
      ]
    },
    {
      "id": "1002",
      "kind": "analytics#accountSummary",
      "name": "Account B",
      "webProperties": [
        {
          "kind": "analytics#webPropertySummary",
          "id": "UA-1002-1",
          "name": "WebProperty B.A",
          "internalWebPropertyId": "3002",
          "level": "STANDARD",
          "websiteUrl": "http://example.com",
          "profiles": [
            {
              "kind": "analytics#profileSummary",
              "id": "2010",
              "name": "Profile B.A.A",
              "type": "WEB"
            }
          ]
        }
      ]
    },
    {
      "id": "1003",
      "kind": "analytics#accountSummary",
      "name": "Account C",
      "webProperties": [
        {
          "kind": "analytics#webPropertySummary",
          "id": "UA-1003-1",
          "name": "WebProperty C.A",
          "internalWebPropertyId": "3003",
          "level": "STANDARD",
          "websiteUrl": "http://example.com",
          "profiles": [
            {
              "kind": "analytics#profileSummary",
              "id": "2011",
              "name": "Profile C.A.A",
              "type": "WEB"
            }
          ]
        }
      ]
    },
    {
      "id": "1004",
      "kind": "analytics#accountSummary",
      "name": "Account D (Property-less)"
    },
    {
      "id": "1005",
      "kind": "analytics#accountSummary",
      "name": "Account E (View-less)",
      "webProperties": [
        {
          "kind": "analytics#webPropertySummary",
          "id": "UA-1005-1",
          "name": "WebProperty E.A (View-less)",
          "internalWebPropertyId": "3005",
          "level": "STANDARD",
          "websiteUrl": "http://example.com"
        }
      ]
    }
  ]
};

export default class ViewSelector extends Component {

  state = {}

  componentDidMount() {
    this.handleAccountChange({target: {value: '1001'}});
  }

  handleAccountChange = ({target}) => {
    const accounts = accountSummaries.items;
    const [account] = accounts.filter((account) => account.id === target.value);
    const properties = account.webProperties;
    const property = properties[0];
    const views = property.profiles;
    const view = views[0];

    this.setState({
      accounts,
      account,
      properties,
      property,
      views,
      view,
    });
  }

  handlePropertyChange = ({target}) => {
    const [property] = this.state.properties.filter((property) => property.id === target.value);
    const views = property.profiles;
    const view = views[0];

    this.setState({
      property,
      views,
      view,
    });
  }

  handleViewChange = ({target}) => {
    const [view] = this.state.views.filter((view) => view.id === target.value);

    this.setState({view});
  }

  render() {
    const {accounts, account, properties, property,
        views, view, viewId} = this.state;

    if (!(account && property && view)) return null;

    return (
      <div>
        <select
          onChange={this.handleAccountChange}
          value={account.id || ''}>
          {accounts.map((account, i) => (
            <option key={i} value={account.id}>{account.name}</option>
          ))}
        </select>
        <select
          onChange={this.handlePropertyChange}
          value={property.id || ''}>
          {properties.map((property, i) => (
            <option key={i} value={property.id}>{property.name}</option>
          ))}
        </select>
        <select
          onChange={this.handleViewChange}
          value={view.id || ''}>
          {views.map((view, i) => (
            <option key={i} value={view.id}>{view.name}</option>
          ))}
        </select>
      </div>
    )
  }
}
bug

Most helpful comment

All 9 comments

Thanks for adding the codepen, will look into this ASAP. I think I was seeing a similar issue with the language picker <select> on the preact site.

Update: I think this is caused by props being diffed prior to children, a behavior that changed in 7.x.

While working with #454, I had some doubt about this.
Now its becoming more clear.

I request to review the commit e3b8fd42dea1609ccccb977f03c20ed8922192a5 again.

I think, here is the problem

@harish2704 Yup, I had messaged you on Gitter when I saw your fork haha.
I'm really hoping switching the order of attributes/children diff fixes a few things.

OMG.. I really missed the world of Gitter . Thanks Bro...

Regarding the bug.
You mean, While keeping the element in cache, if we decide not to remove extra things in element, I think it will get fixed.

That's the hope. I'll try to update the relevant issues when I've done a bunch of testing around it.

Thanks @developit

I was about to open an issue for the same problem and found this.

Prioritizing it, sorry for the delay :)

This is fixed in the next release, hopefully out tonight! Hooray!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marcosguti picture marcosguti  路  3Comments

matthewmueller picture matthewmueller  路  3Comments

rajaraodv picture rajaraodv  路  3Comments

nopantsmonkey picture nopantsmonkey  路  3Comments

adriaanwm picture adriaanwm  路  3Comments