React.js: загрузка данных JSON с помощью API-интерфейса Fetch и реквизита из массива объектов

Полностью новый для response.js, и после прохождения учебника и чтения документов я все еще немного борюсь с использованием js fetch для загрузки моих данных из файла JSON, а также для задания свойств из массива объектов. Я также не уверен, что правильно получаю свойства DOM в обработчике событий. Мне нужно пропустить что-то довольно простое.

Для справки здесь мой код с остальной частью проекта и вот как он должен выглядеть.

ETA: Я не знал, из документов, что браузер Babel был устаревшим, поэтому решил просто использовать прямой Javascript с синтаксисом ES5 вместо JSX. Код обновлен ниже, но он по-прежнему не выполняет разметку.

var CanvasAnimation = React.createClass({
    getInitialState: function() {
        return {data: []};
    },                            
    loadData: function() {
        /*
        fetch("data.json")
            .then(function(response) {
                return response.json    
                    .then(function(json){
                        this.setState({data: json});
                    }.bind(this))
            }.bind(this));
        */
        const data = [
            { id: "stalkerOne", width: 225, height: 434, spriteSheetURL: 'spriteSheets/stalkerone.jpg', rows: 5, columns: 5, totalFrames: 24 },
            { id: "stalkerTwo", width: 175, height: 432, spriteSheetURL: 'spriteSheets/stalkertwo.jpg', rows: 6, columns: 5, totalFrames: 26 },
            { id: "stalkerThree", width: 251, height: 432, spriteSheetURL: 'spriteSheets/stalkerthree.jpg', rows: 6, columns: 5, totalFrames: 28 }
        ];
    },
    componentDidMount: function() {
        this.loadData();
    },
    componentDidUpdate: function() {
        function animation(json) {
            return json.map(function(data) {
                return(
                    new CanvasSprite(
                        document.getElementById(data.id),
                        data.width,
                        data.height,
                        data.spriteSheetURL,
                        data.rows,
                        data.columns,
                        data.totalFrames)
                );
            });
        };
        this.setState({animaton: animation(this.state.data)});  
    },
    handleInteraction: function(event, index) {
        var offsetY = event.clientY - event.node.getBoundingClientRect().top;
        var relY = offsetY/this.state.data.height;
        this.props.animation[index].setFrame(relY);
    },
    render: function() {
        var canvases = this.state.data.map(function(data, index) {
            return (
                React.createElement('canvas', 
                                    id = data.id,
                                    width = data.width,
                                    height = data.height,
                                    style = 'border:5px solid white',
                                    onMouseOver= this.handleInteraction(event, index))
            );
        });
        return(
            React.createElement('div', canvases)
        );
    }
});
  
    
ReactDOM.render(
    React.createElement(CanvasAnimation, null),
    document.getElementById('content')
); 

Ответы

Ответ 1

Хорошо... Здесь рабочий проект. Получил некоторую помощь от @gumingfeng и @hkal.

Извлеченные уроки:

  • Документы React смехотворно устарели.
  • Прямой JS против JSX действительно не хуже imo.
  • Исправлены некоторые синтаксические ошибки в Fetch.
  • Массив объектов должен быть создан после загрузки данных, чтобы передавать DOM-ссылки в конструкторах.
  • Однако вызов setState() внутри componentDidUpdate() запускает бесконечный цикл, поэтому ему нужно было установить массив объектов напрямую и независимо от состояния.
  • При создании элементов DOM из массива React автоматически не назначает обработчик события конкретному элементу. Другими словами, он должен быть передан индекс массива, чтобы его можно было использовать для доступа к его значениям в массивах.

Я думаю, что это так. Надеюсь, это поможет другим.

И я бы просто закончил, сказав: дайте React попробовать без JSX. Это действительно не так уж плохо:)

const { Component } = React;
const { render } = ReactDOM;

class CanvasAnimation extends Component {
    
    constructor(){
        super();
        this.state = {
            data: []
        };
    };
    
    componentDidMount() {
        fetch("data.json")
            .then( (response) => {
                return response.json() })   
                    .then( (json) => {
                        this.setState({data: json});
                    });
    };
    
    componentDidUpdate() {
        function animation(json) {
            return json.map( (data) => {
                return(
                    new CanvasSprite(
                        document.getElementById(data.id),
                        data.width,
                        data.height,
                        data.spriteSheetURL,
                        data.rows,
                        data.columns,
                        data.totalFrames)
                );
            });
        };
        //this.setState({animation: animation(this.state.data)}); //causes infinite loop
        this.animation = animation(this.state.data);
    };
    
    handleInteraction(event, index) {
        var offsetY = event.clientY -  document.getElementById(this.state.data[index].id).getBoundingClientRect().top;
        var relY = offsetY/this.state.data[index].height;
        this.animation[index].setFrame(relY);
    };
    
    render() {
        var canvases = this.state.data.map( (data, index) => {
            return (
                React.createElement('canvas', 
                                    {id : data.id,
                                    width : data.width,
                                    height : data.height,
                                    //style : {border: '5px solid white'},
                                    onMouseMove : (event) => this.handleInteraction(event, index)}
                                    )
            );
        });
        return(
            React.createElement('div', null, ...canvases)
        );
    };
    
};
  
    
render(
    React.createElement(CanvasAnimation, null),
    document.getElementById('content')
);

Ответ 2

У вас есть тонны синтаксических ошибок в вашем коде, я исправил их для вас.

const { Component } = React;
const { render } = ReactDOM;

class CanvasAnimation extends Component {
  state = {
    data: []
  };

  loadData() {
    function animation(json) {
      return json.map(function(data) {
        return (
          new CanvasSprite(
            document.getElementById(data.id),
            data.width,
            data.height,
            data.spriteSheetURL,
            data.rows,
            data.columns,
            data.totalFrames
          )
        );
      });
    }
    fetch("data.json")
      .then(response => response.json())
      .then(json => {
        console.log(json);
        this.setState({
          data: json,
          animation: animation(json)
        });
      });
  }

  componentDidMount() {
    this.loadData();
  }

  handleInteraction(e) {
    var offsetY = e.clientY - e.node.getBoundingClientRect().top;
    var relY = offsetY/this.state.data.height;
    this.props.animation.setFrame(relY);
  }

  render() {
    var canvases = this.state.data.map(function(data) {
      return (
        <canvas
          id={data.id} 
          width={data.width} 
          height={data.height}
          style={{border: '5px white'}}
          onMouseOver={this.handleInteraction}
        />
      );
    });

    return (
      <div>{canvases}</div>
    );
  }
}

render(
  <CanvasAnimation />,
  content
);

Я не знаю ответа вашего API, поэтому я не уверен, если там что-то исправить.

Некоторые из проблем, которые я заметил:

  • Вероятно, ваш отступ неправильный, потому что у вас есть функции с двойными операторами return. Я предлагаю вам включить ESLint в вашей среде IDE, чтобы поймать эти ошибки.

  • Вы не поняли, как работает setState, вы не можете просто делать:

    this.setState({
      foo: 'bar',
      baa: myFn(this.state.foo)
    });
    

    В противном случае this.state.foo внутри myFn будет ссылаться на его старое значение, а не на новое, которое вы устанавливаете прямо сейчас.
    Вам нужно будет сделать this.setState({foo: 'bar'}, () => this.setState({baa: myFn(this.state.foo)}), но тогда лучше сделать то же, что я сделал в коде, который я исправил выше.