Kullanıcı Arayüzünü Tamamen Senkronize Tutarken Dosyaları Tepki Halinde Yükleme – JSManifest

Kullanıcı Arayüzünü Tamamen Senkronize Tutarken Dosyaları Tepki Halinde Yükleme – JSManifest

Bir dosya yükleme bileşeni oluşturmak, kullanıcıların yerel ortamlarının dışında dosya seçip göndermelerine olanak tanıdığı için öğrenilmesi gereken çok önemli bir beceridir.

Bununla birlikte, bu gönderi yerele yoğun bir şekilde odaklanıyor dosya JavaScript’te api. API dosyasının nasıl çalıştığı hakkında biraz daha fazla bilgi edinmek istiyorsanız, tıklayın burada.

İlk başta, bir dosya yükleme bileşeni oluşturmak, özellikle görünüşünü ve verdiği hissi özelleştirmek istiyorsanız, kavraması zor bir kavram olabilir. (Gelecekteki bir eğitimde özel tasarım dosyası girdi bileşenlerini gözden geçirebiliriz). Ama kavramları bir kez iyi anladığınızda, aslında o kadar kötü değil mi!

Seni kastediyorum Yapabilmek sadece bir dosya giriş öğesi oluşturun, bir onChange iletin ve oradan bir gün arayın. Ancak, kullanıcılarınıza mevcut durumu sunarak onlarla ilgilenecek misiniz? durum sürecin her şekilde? Yoksa orada oturup, aralarında herhangi bir görsel güncelleme olmadan sonunu görmelerini ummalarına izin mi vereceksiniz?

Kullanıcının internet bağlantısı kesilirse ne olur? Sunucu hiçbir şeyle yanıt vermezse ne olur? Ya dosya 8/14 onlar için büyükse? Kullanıcı yükleme işleminin 10 dakika boyunca bitmesini bekliyorsa ve oradan ne kadar ilerlediğini görmek isterse ne olur? Veya hangi dosyalar zaten yüklendi?

Sen Sahip olmak UX’in tutarlı kalmasını istiyorsanız, kullanıcıyı arka planda neler olduğu konusunda sürekli olarak güncel tutmak için. Profesyonel, tutarlı bir kullanıcı arayüzü, uygulamanızla teknolojik bir bakış açısıyla güven oluşturmanıza yardımcı olur. Kullanıcıların kaydolduğu ve bazı hizmetleriniz için ödeme yaptığı bir uygulamaya sahip olmayı planlıyorsanız, teknolojiye güven onlara sunduğunuz ve teknolojinizin herkesten daha iyi olduğu. React’te gelişiyorsunuz, yükselmek ve ötesine geçmek için tüm gücünüz var!

Ama nereden başlamalıyım?

Endişelenme! Bu yazı öğretecek sen Kullanıcıların dosyalarını seçip bir yere göndermelerine yardımcı olurken, aynı zamanda arayüzün içine girmesine izin verecek bir dosya yükleme bileşeni içeren bir kullanıcı arayüzü nasıl oluşturulur? her somutlaştırma anından sonuna kadar güncelleyin. Bileşeni oluşturmak bir şeydir, ancak kullanıcı arayüzünün tüm süreç boyunca durum güncellemeleriyle senkronize olması farklı bir hikaye.

Hadi başlayalım!

Bu derste, hızlı bir şekilde bir tepki projesi oluşturacağız. oluştur-tepki-uygulaması.

Devam edin ve aşağıdaki komutu kullanarak bir proje oluşturun. Bu eğitim için onu arayacağım ux ile dosya yükleme

npx create-react-app file-upload-with-ux

Şimdi bittiğinde dizine gidin:

İlk yapacağımız şey açılmak App.js ve varsayılan kodu kendi uygulamamızla değiştirin:

kaynak/App.js

import React from 'react'
import './App.css'

const Input = (props) => (
  <input type="file" name="file-input" multiple {...props} />
)

const App = () => {
  const onSubmit = (e) => {
    e.preventDefault()
  }

  const onChange = (e) => {
    console.log(e.target.files)
  }

  return (
    <div className="container">
      <form className="form" onSubmit={onSubmit}>
        <div>
          <Input onChange={onChange} />
          <button type="submit">Submit</button>
        </div>
      </form>
    </div>
  )
}

export default App

Burada bir tanımladık biçim eleman ve bir onSubmit işleyici, böylece kullanıcının gönder tuşuna bastıktan sonra seçtiği tüm dosyalara erişebiliriz.

Formun içinde, kullanıcının dosyalarından herhangi birini seçmesini sağlayacak dosya giriş bileşenini tanımladık. Giriş bir onChange işleyici, bu yüzden onu da iletiyoruz. onChange işleyicisi, erişerek dosyaları alabilecektir. e.target.files ilk argümanın içinde.

İçeride bazı temel stiller uyguladım App.css. Bunları kullanmayı seçebilir veya bu adımı atlayabilirsiniz:

App.css

.container {
  padding: 8px;
  width: 100%;
  box-sizing: border-box;
  overflow-x: hidden;
}

.form {
  position: relative;
  width: 100%;
  height: 100%;
}

.form input,
button {
  margin-bottom: 15px;
}

.form button {
  padding: 8px 17px;
  border: 0;
  color: #fff;
  background: #265265;
  cursor: pointer;
}

.form button:hover {
  background: #1e3d4b;
}

Bu yüzden, işleyicileri yerleştirmiş olarak kurulmuş temel bir bileşenimiz var. Şimdi, tüm kirli durum mantığını içeriye, UI bileşenlerinden uzağa yerleştirebilmemiz için özel bir tepki kancası oluşturacağız.

bunu arayacağım useFileHandlers.js:

src/useFileHandlers.js

import React from 'react'

const initialState = {
  files: [],
  pending: [],
  next: null,
  uploading: false,
  uploaded: {},
  status: 'idle',
}

const useFileHandlers = () => {
  return {}
}

export default useFileHandlers

Tüm bu yazının en önemli kısmı muhtemelen başlangıç ​​hali Yukarıda verilen. Bu, kullanıcı arayüzünün dosya yükleme işleminin her anından faydalanmasını sağlayacak olan şeydir.

Dosyalar kullanıcının başlangıçta bir yük yüklediği yerdir. dizi dosya girişinden seçerek dosyaların.

Bekliyor kullanıcı arayüzünü bilgilendirmek için kullanılacak ne dosya şu anda işleniyor ve kaç dosyalar kaldı.

sonraki sonraki öğeye atanacak Bekliyor dizi, kod bunu yapmaya hazır olduğunu algıladığında.

yükleniyor dosyaların hala yüklenmekte olduğunu bilmek için kod için kullanılacaktır.

yüklendi yükleme biter bitmez dosyaları eklediğimiz nesne olacaktır.

Ve sonunda, durum için ekstra kolaylık olarak sağlanmaktadır. Kullanıcı arayüzü lehine kullanmaktır.

kullanacağız kullanımRedüktör api’yi tepkiden kancalayın çünkü kullanımımız için mükemmel.

Ama önce, yukarıdaki bazı sabitleri tanımlayalım. useFileHandlers kanca, böylece durum güncellemelerini uygularken daha sonra hiçbir şeyi yanlış yazmadığımızdan emin oluruz:

src/useFileHandlers.js

const LOADED = 'LOADED'
const INIT = 'INIT'
const PENDING = 'PENDING'
const FILES_UPLOADED = 'FILES_UPLOADED'
const UPLOAD_ERROR = 'UPLOAD_ERROR'

Bunlar girilecek redüktör bu ilk argüman olarak iletilir kullanımRedüktör.

Şimdi redüktörü tanımlamak için:

src/useFileHandlers.js

const reducer = (state, action) => {
  switch (action.type) {
    default:
      return state
  }
}

Muhtemelen ithal etmeyi unutmamalıyız kullanımRedüktör şimdi tepkiden, ha?

src/useFileHandlers.js

import { useReducer } from 'react'

Şimdi durum/sevk api’sini kancaya tanımlamak için:

src/useFileHandlers.js

const useFileHandlers = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return {}
}

export default useFileHandlers

Şimdi eskiye geri döneceğiz onChange daha önce belirlediğimiz ve daha da geliştirdiğimiz uygulama.

Bunu yapmadan önce redüktöre yeni bir switch case ekleyelim:

src/useFileHandlers.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    default:
      return state
  }
}

Bu, onChange işleyicisinin, şu çağrıyı yaptığı anda dosyaları duruma geçirmesine olanak tanır:

src/useFileHandlers.js

const onChange = (e) => {
  if (e.target.files.length) {
    const arrFiles = Array.from(e.target.files)
    const files = arrFiles.map((file, index) => {
      const src = window.URL.createObjectURL(file)
      return { file, id: index, src }
    })
    dispatch({ type: 'load', files })
  }
}

Burada dikkat edilmesi gereken bir şey e.target.files olay nesnesinden aldığımızda bir dizi değil– Dosya Listesi.

Bunu bir diziye dönüştürmemizin nedeni, UI bileşenlerinin bunları eşleyebilmesi ve dosya boyutları ve dosya türleri gibi yararlı bilgileri gösterebilmesidir. Aksi takdirde, bileşenler, eşlemeye çalışırken uygulamanın çökmesine neden olur Dosya Listesis.

Şimdiye kadar, özel kancamızın tüm uygulaması:

src/useFileHandlers.js

import { useReducer } from 'react'


const LOADED = 'LOADED'
const INIT = 'INIT'
const PENDING = 'PENDING'
const FILES_UPLOADED = 'FILES_UPLOADED'
const UPLOAD_ERROR = 'UPLOAD_ERROR'

const initialState = {
  files: [],
  pending: [],
  next: null,
  uploading: false,
  uploaded: {},
  status: 'idle',
}

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    default:
      return state
  }
}

const useFileHandlers = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  const onChange = (e) => {
    if (e.target.files.length) {
      const arrFiles = Array.from(e.target.files)
      const files = arrFiles.map((file, index) => {
        const src = window.URL.createObjectURL(file)
        return { file, id: index, src }
      })
      dispatch({ type: 'load', files })
    }
  }

  return {}
}

export default useFileHandlers

Şimdi diğer işleyiciye odaklanacağız–onSubmit. Bu, kullanıcı formu gönderdiğinde (belli ki) çağrılır. İçinde onSubmit işleyici ile sarıyoruz geri arama kullan böylece her zaman en son durum değerlerini alacaktır.

src/useFileHandlers.js

import { useCallback, useReducer } from 'react'

src/useFileHandlers.js

const onSubmit = useCallback(
  (e) => {
    e.preventDefault()
    if (state.files.length) {
      dispatch({ type: 'submit' })
    } else {
      window.alert("You don't have any files loaded.")
    }
  },
  [state.files.length],
)

Bu onSubmit işleyicisi sonrasında onChange, böylece dosyaları sadece durum.dosyaları bu sadece tarafından ayarlandı onChangeyükleme işlemini başlatmak için.

Ve yükleme sürecini somutlaştırmak için bunun için başka bir geçiş durumuna ihtiyacımız var:

src/useFileHandlers.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    case 'submit':
      return { ...state, uploading: true, pending: state.files, status: INIT }
    default:
      return state
  }
}

Tamam, şimdi ne oluyor:

  1. Değişir durum.yükleme doğru. state.uploading’i değiştirdiğinizde doğruUI bileşenleriyle hasara yol açmaya başlayabilir ve dosyaların yüklenmekte olduğuna dair bir mesaj iletmeye çalıştığınızı anladıkları sürece, kullanıcıya istediğiniz her şeyi görüntüleyebilirsiniz.

  2. başlatır durum.beklemede kullanıcının seçtiği tüm dosyalarla birlikte. Bununla da UI bileşenleriyle devam edebilir ve hasara yol açabilirsiniz. Devletin bu kısmını kullanmanın pek çok yolu var. Ancak şimdilik, bu kısmı atlayacağım çünkü tüm öğreticiyi önce sizinle birlikte geçmek istiyorum 🙂

  3. Devletin kolaylık kısmını belirler, durum ile “İÇİNDE”. Bunu, bazılarını tetiklemek için kancada veya kullanıcı arayüzünde de kullanabilirsiniz “onStart“Mantık ya da ne istersen – çünkü yeni bir yükleme işlemi başlayana kadar asla bu değere geri dönmeyecek.

Şimdi, kullanıcı arayüzünün bunlara mutlu bir şekilde erişebilmesi için durumları ve onSubmit ve onChange işleyicisini geri döndüreceğiz:

src/useFileHandlers.js

return {
  ...state,
  onSubmit,
  onChange,
}

src/useFileHandlers.js

Ele alacağımız bir sonraki şey, kullanım Etkisi Bölüm. Biz ihtiyaç “tamamlamaya kadar” işlevselliğini kolaylaştırmak için useEffect.

Bu useEffect’ler, bu öğreticide oldukça önemli bir uygulamadır, çünkü UI ile özel kanca arasında mükemmel, tutarlı bir senkronize akış oluşturan şeylerdir.her yerde birazdan göreceğiniz gibi.

src/useFileHandlers.js

import { useCallback, useEffect, useReducer } from 'react'

kendimizi tanımlayacağız ilk kullanımEtkisi yüklenecek bir sonraki dosyanın hazır olduğunu tespit eder etmez (içinde hala öğeler olduğu sürece) kolaylaştırılmasından sorumlu olacaktır. durum.beklemede):

src/useFileHandlers.js


useEffect(() => {
  if (state.pending.length && state.next == null) {
    const next = state.pending[0]
    dispatch({ type: 'next', next })
  }
}, [state.next, state.pending])

içindeki bir sonraki kullanılabilir dosyayı alır. durum.beklemede dizi ve kullanarak bir sinyal oluşturur sevkdosyayı bir sonraki olarak gönderme durum.sonraki nesne:

src/useFileHandlers.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    case 'submit':
      return { ...state, uploading: true, pending: state.files, status: INIT }
    case 'next':
      return {
        ...state,
        next: action.next,
        status: PENDING,
      }
    default:
      return state
  }
}

ekledik Durum: Beklemede yine kolaylık olsun diye. Ancak, yükleme işleminin bu kısmını halletmek tamamen size kalmış!

Bu sonraki pasaj, yalnızca bu eğitim için görmeniz için konsolda oturum açmanıza yardımcı olmak için sağladığım bir yardımcı işlevi gösterecek.

src/useFileHandlers.js

const logUploadedFile = (num, color = 'green') => {
  const msg = `%cUploaded ${num} files.`
  const style = `color:${color};font-weight:bold;`
  console.log(msg, style)
}

bu ikinci kullanımEtkisi sonra başvuracağız sorumlu olacak yükleniyor en sonraki durumda ayarlanmış dosya:

src/useFileHandlers.js

const countRef = useRef(0)


useEffect(() => {
  if (state.pending.length && state.next) {
    const { next } = state
    api
      .uploadFile(next)
      .then(() => {
        const prev = next
        logUploadedFile(++countRef.current)
        const pending = state.pending.slice(1)
        dispatch({ type: 'file-uploaded', prev, pending })
      })
      .catch((error) => {
        console.error(error)
        dispatch({ type: 'set-upload-error', error })
      })
  }
}, [state])

İçinde .sonra() işleyici Yeni bir değişken oluşturdum önceki ve onu atadı sonraki yüklemeyi bitiren nesne. Bu sadece okunabilirlik amaçlıdır çünkü birazdan göreceğimiz gibi, anahtar durumlarında kafanın karışmasını istemiyoruz.

olduğunu fark etmiş olabilirsiniz. kullanımRef oraya gizlice girdi. Evet, itiraf ediyorum. Yaptım. Ama bunu yapmamın nedeni, onu gelecek için kullanıp mutasyona uğratacak olmamız. logUploadedDosya sağladığım fayda işlevi.

src/useFileHandlers.js

import { useCallback, useEffect, useReducer, useRef } from 'react'

Oh, ve snippet’te görüldüğü gibi bir “yükleme” söz işleyicisini simüle etmek için bazı sahte işlevlere ihtiyacınız varsa, şunu kullanabilirsiniz:

const api = {
  uploadFile({ timeout = 550 ) {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve()
      }, timeout)
    })
  },
}

Şimdi devam edin ve aşağıdakileri uygulayarak redüktörünüzü güncelleyin. ‘dosya yüklendi’ ve ‘kurulum-yükleme-hatası’ kasaları değiştir:

src/useFileHandlers.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    case 'submit':
      return { ...state, uploading: true, pending: state.files, status: INIT }
    case 'next':
      return {
        ...state,
        next: action.next,
        status: PENDING,
      }
    case 'file-uploaded':
      return {
        ...state,
        next: null,
        pending: action.pending,
        uploaded: {
          ...state.uploaded,
          [action.prev.id]: action.prev.file,
        },
      }
    case 'set-upload-error':
      return { ...state, uploadError: action.error, status: UPLOAD_ERROR }
    default:
      return state
  }
}

İçin dosya yüklendi durumda, sıfırladık sonraki geri hükümsüz böylece ilk kullanımEtkisi tekrar cevap verebilir. Bunu yaptığında, sonraki dosyayı çekecektir. durum.beklemede sıraya alın ve bunu bir sonrakine atayın durum.sonraki değer. Bunun nasıl kendi kendine işleyen bir süreç haline geldiğini şimdiden görmeye başlayabilirsiniz. tamamlamaya koş uygulama!

Her neyse, yeni yüklenen dosyayı şuraya uyguluyoruz. durum.yüklendi UI’nin bunu kendi avantajları için kullanabilmesi için nesne. Bu aynı zamanda bu eğitimde gerçekten faydalı bir özelliktir çünkü bir grup küçük resim oluşturuyorsanız, her satırı anında yüklendikten sonra gölgelendirebilirsiniz! 🙂 Ekran görüntüleri bu yazının sonunda.

bu üçüncü kullanımEtkisi göndererek yükleme işlemini kapatmaktan sorumlu olacaktır. dosyalar yüklendi redüktöre sinyal:

src/useFileHandlers.js


useEffect(() => {
  if (!state.pending.length && state.uploading) {
    dispatch({ type: 'files-uploaded' })
  }
}, [state.pending.length, state.uploading])

Bunu redüktöre eklemek şuna benzer:

src/useFileHandlers.js

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    case 'submit':
      return { ...state, uploading: true, pending: state.files, status: INIT }
    case 'next':
      return {
        ...state,
        next: action.next,
        status: PENDING,
      }
    case 'file-uploaded':
      return {
        ...state,
        next: null,
        pending: action.pending,
        uploaded: {
          ...state.uploaded,
          [action.prev.id]: action.prev.file,
        },
      }
    case 'files-uploaded':
      return { ...state, uploading: false, status: FILES_UPLOADED }
    case 'set-upload-error':
      return { ...state, uploadError: action.error, status: UPLOAD_ERROR }
    default:
      return state
  }
}

Ve özel kanca ile işimiz bitti! Yaşasın!

İşte özel kanca için son kod:

src/useFileHandlers.js

import { useCallback, useEffect, useReducer, useRef } from 'react'

const api = {
  uploadFile({ timeout = 550 }) {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve()
      }, timeout)
    })
  },
}

const logUploadedFile = (num, color = 'green') => {
  const msg = `%cUploaded ${num} files.`
  const style = `color:${color};font-weight:bold;`
  console.log(msg, style)
}


const LOADED = 'LOADED'
const INIT = 'INIT'
const PENDING = 'PENDING'
const FILES_UPLOADED = 'FILES_UPLOADED'
const UPLOAD_ERROR = 'UPLOAD_ERROR'

const initialState = {
  files: [],
  pending: [],
  next: null,
  uploading: false,
  uploaded: {},
  status: 'idle',
}

const reducer = (state, action) => {
  switch (action.type) {
    case 'load':
      return { ...state, files: action.files, status: LOADED }
    case 'submit':
      return { ...state, uploading: true, pending: state.files, status: INIT }
    case 'next':
      return {
        ...state,
        next: action.next,
        status: PENDING,
      }
    case 'file-uploaded':
      return {
        ...state,
        next: null,
        pending: action.pending,
        uploaded: {
          ...state.uploaded,
          [action.prev.id]: action.prev.file,
        },
      }
    case 'files-uploaded':
      return { ...state, uploading: false, status: FILES_UPLOADED }
    case 'set-upload-error':
      return { ...state, uploadError: action.error, status: UPLOAD_ERROR }
    default:
      return state
  }
}

const useFileHandlers = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  const onSubmit = useCallback(
    (e) => {
      e.preventDefault()
      if (state.files.length) {
        dispatch({ type: 'submit' })
      } else {
        window.alert("You don't have any files loaded.")
      }
    },
    [state.files.length],
  )

  const onChange = (e) => {
    if (e.target.files.length) {
      const arrFiles = Array.from(e.target.files)
      const files = arrFiles.map((file, index) => {
        const src = window.URL.createObjectURL(file)
        return { file, id: index, src }
      })
      dispatch({ type: 'load', files })
    }
  }

  
  useEffect(() => {
    if (state.pending.length && state.next == null) {
      const next = state.pending[0]
      dispatch({ type: 'next', next })
    }
  }, [state.next, state.pending])

  const countRef = useRef(0)

  
  useEffect(() => {
    if (state.pending.length && state.next) {
      const { next } = state
      api
        .uploadFile(next)
        .then(() => {
          const prev = next
          logUploadedFile(++countRef.current)
          const pending = state.pending.slice(1)
          dispatch({ type: 'file-uploaded', prev, pending })
        })
        .catch((error) => {
          console.error(error)
          dispatch({ type: 'set-upload-error', error })
        })
    }
  }, [state])

  
  useEffect(() => {
    if (!state.pending.length && state.uploading) {
      dispatch({ type: 'files-uploaded' })
    }
  }, [state.pending.length, state.uploading])

  return {
    ...state,
    onSubmit,
    onChange,
  }
}

export default useFileHandlers

Ama bekleyin, daha bitmedi. Hala bu mantığı kullanıcı arayüzüne uygulamamız gerekiyor. Kahretsin!

ithal edeceğiz useFileHandlers kanca ve bileşende kullanın. Ayrıca her dosyanın üzerinde UI haritası oluşturacağız ve bunları küçük resimler olarak oluşturacağız:

kaynak/App.js

import React from 'react'
import useFileHandlers from './useFileHandlers'
import './App.css'

const Input = (props) => (
  <input
    type="file"
    accept="image/*"
    name="img-loader-input"
    multiple
    {...props}
  />
)

const App = () => {
  const {
    files,
    pending,
    next,
    uploading,
    uploaded,
    status,
    onSubmit,
    onChange,
  } = useFileHandlers()

  return (
    <div className="container">
      <form className="form" onSubmit={onSubmit}>
        <div>
          <Input onChange={onChange} />
          <button type="submit">Submit</button>
        </div>
        <div>
          {files.map(({ file, src, id }, index) => (
            <div key={`thumb${index}`} className="thumbnail-wrapper">
              <img className="thumbnail" src={src} alt="" />
              <div className="thumbnail-caption">{file.name}</div>
            </div>
          ))}
        </div>
      </form>
    </div>
  )
}

export default App

Bu temel bileşen, yüklendiğinde bir sürü küçük resim oluşturur. Stillere çok takılmadım çünkü eğlenmeyi size bırakıyorum 🙂

Ancak burada temel stilleri kullanmak istiyorsanız bunlar:

kaynak/App.css

.thumbnail-wrapper {
  display: flex;
  align-items: center;
  padding: 6px 4px;
}

.thumbnail {
  flex-basis: 100px;
  height: 100%;
  max-width: 50px;
  max-height: 50px;
  object-fit: cover;
}

.thumbnail-caption {
  flex-grow: 1;
  font-size: 14px;
  color: #2b8fba;
  margin-bottom: 5px;
  padding: 0 12px;
}

Tüm dosyaların yüklenmesi tamamlandığında ne olur? Eh, henüz hiçbir şey gerçekten. Ancak en azından kullanıcıya, bittiğini bilmelerini sağlamak için bir şey gösterebiliriz:

kaynak/App.js

{
  status === 'FILES_UPLOADED' && (
    <div className="success-container">
      <div>
        <h2>Congratulations!</h2>
        <small>You uploaded your files. Get some rest.</small>
      </div>
    </div>
  )
}

kaynak/App.css

.success-container {
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}

.success-container h2,
small {
  color: green;
  text-align: center;
}

Bu zaman, durum burada kullanılıyor. Bak, oldukça faydalı, değil mi? Diğeriyle oldukça şaşırtıcı karmaşık görünümlü bir kullanıcı arayüzü oluşturabilirsiniz. durum değerleri ile birleştirildiğinde de durum.beklemede ve diğerleri. Bu eğitimde harika bir şey yaptıysanız, bana birkaç ekran görüntüsü içeren bir e-posta gönderin!

Nihai çıktı:

kaynak/App.js

import React from 'react'
import useFileHandlers from './useFileHandlers'
import './App.css'

const Input = (props) => (
  <input
    type="file"
    accept="image/*"
    name="img-loader-input"
    multiple
    {...props}
  />
)

const App = () => {
  const {
    files,
    pending,
    next,
    uploading,
    uploaded,
    status,
    onSubmit,
    onChange,
  } = useFileHandlers()

  return (
    <div className="container">
      <form className="form" onSubmit={onSubmit}>
        {status === 'FILES_UPLOADED' && (
          <div className="success-container">
            <div>
              <h2>Congratulations!</h2>
              <small>You uploaded your files. Get some rest.</small>
            </div>
          </div>
        )}
        <div>
          <Input onChange={onChange} />
          <button type="submit">Submit</button>
        </div>
        <div>
          {files.map(({ file, src, id }, index) => (
            <div
              style={{
                opacity: uploaded[id] ? 0.2 : 1,
              }}
              key={`thumb${index}`}
              className="thumbnail-wrapper"
            >
              <img className="thumbnail" src={src} alt="" />
              <div className="thumbnail-caption">{file.name}</div>
            </div>
          ))}
        </div>
      </form>
    </div>
  )
}

export default App

kaynak/App.css

(Mobil cihazlar için medya sorguları dahildir)

.container {
  padding: 8px;
  width: 100%;
  box-sizing: border-box;
  overflow-x: hidden;
}

.form {
  position: relative;
  width: 100%;
  height: 100%;
}

.form input,
button {
  margin-bottom: 15px;
}

.form button {
  padding: 8px 17px;
  border: 0;
  color: #fff;
  background: #265265;
  cursor: pointer;
}

.form button:hover {
  background: #1e3d4b;
}

.thumbnail-wrapper {
  display: flex;
  align-items: center;
  padding: 6px 4px;
}

.thumbnail {
  flex-basis: 100px;
  height: 100%;
  max-width: 50px;
  max-height: 50px;
  object-fit: cover;
}

.thumbnail-caption {
  flex-grow: 1;
  font-size: 14px;
  color: #2b8fba;
  margin-bottom: 5px;
  padding: 0 12px;
}

.success-container {
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}

.success-container h2,
small {
  color: green;
  text-align: center;
}

@media screen and (max-width: 472px) {
  .container {
    padding: 6px;
  }

  .thumbnail-wrapper {
    padding: 6px 2px;
  }

  .thumbnail {
    flex-basis: 40px;
    width: 100%;
    height: 100%;
    max-height: 40px;
    max-width: 40px;
  }

  .thumbnail-caption {
    font-size: 12px;
  }
}

Ekran görüntüleri

bazı ekran görüntülerini sağladım temel Bu eğitimdeki kodu kullanarak UX uygulaması:

onChange

logUploadedFile()

logUploadedDosya

durum.beklemede

durum beklemede

Çözüm

Bu, bu yazının sonunu tamamlıyor. Umarım beğenmişsinizdir ve daha kaliteli gönderiler için takipte kalın! 🙂

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.