Динамические фоновые изображения React/Gatsby в компоненте

У меня есть компонент React, который по существу делает связанный контент, показывая пользователю следующую/предыдущую части контента внутри сайта.

Этот компонент должен импортировать фоновые изображения, чтобы я мог использовать их как встроенные стили внутри компонента. Однако, чтобы узнать, какое фоновое изображение я хочу импортировать, я должен посмотреть на свойства, которые определяются следующим образом:

<LocationRelated 
  previousLocationName="Columbus, OH"
  previousLocationPath="columbus"
  nextLocationName="St. Pete Beach, FL"
  nextLocationPath="st-pete-beach"
/>

Но, насколько я могу судить, нет способа просмотреть свойства компонента до того, как он войдет в функцию React render, поэтому нет возможности выполнить динамический импорт.

Это моя неудачная попытка сделать это, используя литерал шаблона ES6 внутри импорта:

import React from "react"
import pfbRelatedPrevBGImagePath from '`../../images/grid/PFB_${this.props.previousLocationPath}.jpg`'
import pfbRelatedNextBGImagePath from '`../../images/grid/PFB_${this.props.nextLocationPath}.jpg`'

class LocationRelated extends React.Component {

  render() {

    const pfbRelatedPrevBG = { 
      backgroundImage: `linear-gradient( to bottom, rgba(1,1,1,0.2), rgba(2,2,2,0.2) ), url(${ pfbRelatedPrevBGImagePath })` 
    }
    const pfbRelatedNextBG = { 
      backgroundImage: `linear-gradient( to bottom, rgba(1,1,1,0.2), rgba(2,2,2,0.2) ), url(${ pfbRelatedNextBGImagePath })` 
    }

    return (
      <div>
        <aside className="pfb-longform-container">
          <section className="pfb-related-content">
            <h3 className="pfb-related-content-title">Additional Locations</h3>
            <section className="pfb-related-image-container">
              <div 
                className="pfb-related-1" 
                style= { pfbRelatedPrevBG }
              >
                <p className="pfb-related-text">{ this.props.previousLocationName }</p>
              </div>
              <div 
                className="pfb-related-2"
                style= { pfbRelatedNextBG }
              >
                <p className="pfb-related-text">{ this.props.nextLocationName }</p>
              </div>
            </section>
          </section>
        </aside>
      </div>
    )
  }
}

export default LocationRelated

Мой компоновщик веб-пакетов выдает ошибку, говоря, что в основном он не может прочитать this.props.whatever, потому что он еще не был определен, когда он пытается прочитать файл import. Есть ли способ сделать это?

Дополнительное примечание: я использую генератор статических сайтов Gatsby для этого сайта, но это не должно сильно влиять на то, что происходит (по крайней мере, я не думаю, что должно).


person serraosays    schedule 20.02.2018    source источник
comment
Вы можете сделать что-то вроде функции рендеринга снаружи return let previousLocation; if(this.props.previousLocationName){previousLocation = this.props.previousLocationName} else previousLocation = <div>Loading</div>, а затем вместо {this.props.previousLocationName} просто дать {previousLocation}   -  person Aaqib    schedule 21.02.2018
comment
@Aaqib - я понимаю, что вы говорите, но как это поможет мне импортировать правильное фоновое изображение в компонент?   -  person serraosays    schedule 21.02.2018
comment
Вы не можете использовать это ключевое слово вне класса.   -  person Jayavel    schedule 21.02.2018
comment
@Jayavel - есть ли другой способ сделать это?   -  person serraosays    schedule 21.02.2018
comment
если вы объясните мне немного больше, что было бы полезно, я вижу, что pfbRelatedPrevBG будет иметь URL-адрес изображения, который вы передаете, а LocationRelated будет иметь реквизиты URL-адреса изображения, это правильно?   -  person Jayavel    schedule 21.02.2018
comment
вы можете использовать ../../images/grid/PFB_${this.props.previousLocationPath}.jpg непосредственно в вашем pfbRelatedPrevBG   -  person Jayavel    schedule 21.02.2018
comment
@Jayavel - это действительно хорошая идея. Я пробовал это, но инструмент Gatsby CMS несколько причудливо уменьшает размер изображения и изменяет пути импорта для производства, но не встроенные пути CSS в функции render().   -  person serraosays    schedule 21.02.2018


Ответы (1)


Вышеописанный подход невозможен, насколько я могу судить, но, как я обнаружил, это невозможно по уважительной причине. Вы должны ПОЛНОСТЬЮ использовать React (Gatsby) как делать вещи, и мой подход выше пытался сделать это с точки зрения сборки Drupal/WordPress-CMS.

Посмотрите мой пересмотренный компонент для LocationRelated:

import React from "react"
import Link from 'gatsby-link'

class LocationRelated extends React.Component {

  render() {

    // Setup inline style objects using ES6 template literals which pull in the correct paths from the content pages themselves
    const pfbRelatedPrevBG = { 
      backgroundImage: `linear-gradient( to bottom, rgba(1,1,1,0.2), rgba(2,2,2,0.2) ), url(${ this.props.prevLocationImgPath.sizes.src })` 
    }
    const pfbRelatedNextBG = { 
      backgroundImage: `linear-gradient( to bottom, rgba(1,1,1,0.2), rgba(2,2,2,0.2) ), url(${ this.props.nextLocationImgPath.sizes.src })` 
    }

    return (
      <div className="pfb-related-bg-container">
        <aside className="pfb-related-container">
          <section className="pfb-related-content">
            <section className="pfb-related-image-container">
              <Link 
                to={ this.props.prevLocationPath }
                className="pfb-related-left-arrow"
              >
                <img src= { LeftArrow } alt="Navigate to the previous location in the PFB Annual Report" />
                <div className="pfb-related-left-arrow-text">Previous</div>
              </Link>
              <Link 
                to={ this.props.prevLocationPath }
                className="pfb-related-1" 
                style={ pfbRelatedPrevBG }
              >
                <p className="pfb-related-text">{ this.props.prevLocationName }</p>
              </Link>
              <Link 
                to={ this.props.nextLocationPath }
                className="pfb-related-2"
                style= { pfbRelatedNextBG }
              >
                <p className="pfb-related-text">{ this.props.nextLocationName }</p>
              </Link>
              <Link 
                to={ this.props.nextLocationPath }
                className="pfb-related-right-arrow"
              >
                <img src= { RightArrow } alt="Navigate to the next location in the PFB Annual Report" />
                <div className="pfb-related-right-arrow-text">Next</div>
              </Link>
            </section>
          </section>
        </aside>
      </div>
    )
  }
}

export default LocationRelated

Вы заметите, что не так уж много отличается от приведенной выше попытки, но обратите внимание на путь this.props.nextLocationImgPath.sizes.src, используемый в литерале объекта backgroundImage. Вы должны передать backgroundImage исходный путь в качестве реквизита. Не пытайтесь сделать это в компоненте, как я. Разделение задач React заключается в том, чтобы получать данные из компонентов и вместо этого вводить данные через реквизиты.

Итак, возникает вопрос: если вам нужно передать путь в качестве реквизита, как вы получите путь, когда вы вызываете свой компонент на уровне страницы?

Если вы используете Gatsby (а вы должны), вам нужно использовать GraphQL для получения оптимизированных путей к изображениям. В моем примере мы будем вызывать путь background-image на той же странице, где мы вызывали бы компонент LocationRelated.

Упрощенный пример моей страницы, посвященной рассматриваемой проблеме:

import React from 'react'
import Img from "gatsby-image";

import LocationRelated from '../components/LocationRelated'

class CharlotteLocation extends React.Component {
  render() {
    return (
      <div>    
        <LocationRelated 
          prevLocationName="Detroit"
          prevLocationPath="detroit"
          prevLocationImgPath= { this.props.data.charlottePrevImage }
          nextLocationName="Montana"
          nextLocationPath="montana"
          nextLocationImgPath= { this.props.data.charlotteNextImage }
        />
      </div>
    )
  }
}

export default CharlotteLocation 

// GraphQL queries for each image
export const CharlotteImageQuery = graphql`
  query CharlotteImageQuery {
    charlottePrevImage: imageSharp(id: { regex: "/PFB_DETROIT/" }) {
      sizes(maxWidth: 600 ) {
        ...GatsbyImageSharpSizes_withWebp
      }
    },
    charlotteNextImage: imageSharp(id: { regex: "/PFB_MONTANA/" }) {
      sizes(maxWidth: 600 ) {
        ...GatsbyImageSharpSizes_withWebp
      }
    }
  }
`

Эти запросы GraphQL делают доступными оптимизированные пути к нужным нам файлам, и теперь мы можем отправить их в наш компонент LocationRelated через this.props.data.

gatsby-image и GraphQL поначалу немного сложны в работе, поэтому ознакомьтесь с это прекрасное руководство от Кайла Гилла. Это заставило меня двигаться немного лучше, чем документация Гэтсби.

Для справки, вот как я настроил свой gatsby-config.js в проекте, чтобы все эти плагины хорошо работали вместе (я извлекаю изображения из автономного каталога images в своем проекте, а не из компонента, поэтому мне нужно было gatsby-source-filesystem, чтобы все эта работа вместе с gatsby-image, gatsby-transformer-sharp, gatsby-plugin-sharp,):

module.exports = {
  siteMetadata: {
    title: 'PFB2017 Site',
  },
  //pathPrefix: "/pfb2017-site",
  plugins: [
    // Adds in React Helmet for metadata
    `gatsby-plugin-react-helmet`,

    // Gives us sass in the project
    `gatsby-plugin-sass`,

    // This plugin transforms JSON, which is how we are storing our location data
    `gatsby-transformer-json`,

    // Adds in Gatsby image handling
    `gatsby-image`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,

    /** 
     * This is how you customize gatsby-source-filesystem
     * Check this page out for more background 
     * https://www.gatsbyjs.org/docs/building-with-components 
     * By default, the gatsby-default starter kit comes with the `pages` directory wired up
     * I'm adding it here for consistency but you don't need it
     * What I have added into the starter are `images` and `data`, subfolders we'll need for the PFB project
     * 
     */

    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `pages`,
        path: `${__dirname}/src/pages`,
      },
    },    
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/src/data`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`
      }
    }
  ],
};
person serraosays    schedule 01.03.2018