Qual a diferença entre usar construtor e usar getInitialState no React Native?

Estudando aqui códigos, já vi os dois sendo usados sem muita distinção.

Quais são os principais casos de uso para ambos? Existem vantagens/desvantagens? Um é uma prática melhor?

A diferença entre constructor e getInitialState é a diferença entre o ES6 e o próprio ES5 em si. getInitialState é usado com React.createClass e
construtor é usado com React.Component.

Daí a questão se resume às vantagens/desvantagens do uso do ES6 ou ES5.

Vamos ver a diferença no código:

ES5

var TodoApp = React.createClass({ 
  propTypes: {
    title: PropTypes.string.isRequired
  },
  getInitialState () { 
    return {
      items: []
    }; 
  }
});

ES6

class TodoApp extends React.Component {
  constructor () {
    super()
    this.state = {
      items: []
    }
  }
};

A comunidade React está se aproximando do ES6. Também é considerado como a melhor prática para o desenvolvimento React no momento.

Existem algumas diferenças entre React.createClass e React.Component. Por exemplo, this é tratado nesses casos. construtor também pode ser usado para lidar com tais situações. Para vincular métodos a uma instância de componente, ela pode ser pré-vinculada no construtor.

A partir das atualizações do React, com o Hooks, isso mudou e não é mais preciso usar um construtor. Por exemplo:

class TodoApp extends React.Component {

    this.state = {items: []}
};

Isso ainda será transformado para o formato do construtor, mas você não precisará se preocupar com isso. Você pode usar este formato que é mais legível.

Já com o Hooks, você nem precisa declarar uma classe mais, podendo ser feita num componente funcional:

import React, { useState } from 'react';

function TodoApp () {
  const items = useState([]);

Observe que o estado inicial é passado como um argumento para useState em useState([]).