React-Redux-Thunk Kullanımına Bir Örnek - 2

Merhaba, bir önceki yazıma devam ediyorum. Okumadıysanız sizi buraya alalım. 

Önceki yazıda, action types ları, actions ları, reducers ları bitirmiştik.

Devam edelim.

store içine gelelim ve index.js dosyası oluşturalım. İçerisine aşağıdaki kodları yazalım. 

// redux tan store oluşturacağımız için createStore fonksiyonunu kullanacağız
// thunk kullanacağımız için de applyMiddleware fonksiyonunu kullanacağız
import { createStore, applyMiddleware } from 'redux'

// asenkron işlemler için 
import thunk from 'redux-thunk'

// Tüm reducer larımızı dahil ediyoruz. 
import allReducers from '../reducers'

// Tarayıcıya redux devtools extension kurduysak oradan gözlemlemek iççin
import { composeWithDevTools } from 'redux-devtools-extension'

// Meşhur kısım burası
const store = createStore(
  allReducers,
  composeWithDevTools(applyMiddleware(thunk)),
)

export default store

Yorum satırları ile daha da anlaşılır olmuştur.  Artık store umuzu oluşturduk. 

Sıra geldi bu store değerine her yerden ulaşmaya.

App.js dosyamızı açalım ve şu şekilde yeniden düzenleyelim.

import './App.css'

// store değerlerine tüm component lerden erişebilmek için gerekli
import { Provider } from 'react-redux'

// Oluşturduğumuz store u dahil ediyoruz. 
import store from './store'

import Users from './components/users/Users'

function App() {

  return (
    <Provider store={store}>
      <div className="container mt-5">
        <Users />
      </div>
    </Provider>
  )
}

export default App

Evet esprimiz yukarıdaki gibidir. 

Artık kullanıcılarımız ile ilgili işlemler yapmaya başlayabiliriz.

Users.jsx dosyamızı açalım. Şu kodları ekleyelim. 

import { useDispatch, useSelector } from 'react-redux'
import actions from '../../actions'

Artık başlıyoruz.

const Users = () => {

  // dispatch fonksiyonu ile actions içinde tanımladığımız fonksiyonları çağırıcaz diyelim
  let dispatch = useDispatch()

  // state imizi alıyoruz.
  let usersState = useSelector((state) => state.userReducer)
 
  // actions içindeki fetchUsers() fonksiyonumuz ile kullanıcılarımızı çekiyoruz.
  // her dispatch işleminde kullanıcılarımızı çekelim.
  useEffect(() => {
    dispatch(actions.fetchUsers())
  }, [dispatch])

Redux devtools ektension kurduysanız F12'ye basın ve oradan da Redux segmesine gidin. 

Şöyle bir şey göreceksiniz.

 

Sanırım daha da anlaşılır olmuştur. 

Sağ taraftaki state segmesini tıklarsanız şöyle bir şey görmelisiniz. 

 

Evet, herhangi bir sorun yok ve kullanıcı bilgilerimiz data içerisinde bulunuyor. 

Dikkat ettiyseniz isLoading değeri false görünüyor. Bir de sol tarafta bulunan FETCH_USER_REQUEST action değerine tıklayalım. Şöyle bir şey ile karşılaşmanız gerekiyor. 

 

 

Dikkat ettiniz mi? Burada da isLoading değeri true. Biz de bunu kullanarak ekrana loading animasyonunu koyabiliriz. Bu cebimizde dursun. 

Bir önceki görselden de anlaşılacağı üzere verilerimiz data içerisinde bulunuyor. Bunu userReduser içerisinde biz tanımlamıştık hatırlarsanız. 

Bu verileri de map() fonksiyonu ile ekrana basabiliriz. 

Aşağıda kodu bulunuyor.

  return (
    <>
      {usersState.isLoading ? (
        <div className="text-center mb-5">
          <i className="fas fa-sync fa-spin"></i>
        </div>
      ) : (
        <>
          <Modal />
          <div className="table-responsive">
            <div className="YeniEkle">
              <button
                data-bs-toggle="modal"
                data-bs-target="#exampleModal"
                data-target=".bd-example-modal-lg"
                className="btn btn-primary float-end"
              >
                Kullacı Ekle
              </button>
            </div>

            {usersState.data.length > 0 ? (
              <table className="table table-hover">
                <thead>
                  <tr>
                    <th scope="col">#</th>
                    <th scope="col">Ad Soyad</th>
                    <th scope="col">Kullanıcı Adı</th>
                    <th scope="col">E-posta</th>
                    <th scope="col">Telefon</th>
                    <th scope="col">Düzenle</th>
                  </tr>
                </thead>
                <tbody>
                  {usersState.data.map((user, index) => (
                    <tr key={index}>
                      <th scope="row">{index + 1}</th>
                      <td>{user.name}</td>
                      <td>{user.username}</td>
                      <td>{user.email}</td>
                      <td>{user.phone}</td>
                      <td className="d-flex justify-content-between">
                        <button
                          className="btn btn-sm btn-warning "
                          onClick={() => handleEditClick(user, index)}
                        >
                          <i className="fas fa-user-edit"></i>
                        </button>

                        <button
                          className="btn btn-sm btn-danger"
                          onClick={() => handleDelete(user.id)}
                        >
                          <i className="fas fa-user-times"></i>
                        </button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            ) : (
              <span className="text-danger -mt-5">
                <b>Herhangi bir kullanıcı bulunmuyor!</b>
              </span>
            )}
          </div>
        </>
      )}
    </>
  )
}

Aşağıdaki gibi bir ekran gelmeli. 

 

Silme işlemini yapalım. Zira en kolayı olacaktır. 

Dikkat ettiyseniz handleDelete() adında bir fonksiyon tanımlamışız ve her kullanıcının id sini gönderiyoruz. O zaman fonksiyonu  return ün üzerinde tanımlayalım. 

let handleDelete = (userId) => {
  
  // Bir kontrol yapalım yine de :)
  if (window.confirm('Silmek istediğinize emin misiniz ?')) {
     
     // actions içindeki deleteUser fonksiyonuna gidiyor.
     dispatch(actions.deleteUser(userId))

  }
}

Silme işlemi düzgün çalışmalı. En azından ümit ediyoruz. 

Gelelim düzenleme işlemine. 

Dikkat ettiyseniz handleEditClick adında bir fonksiyon tanımlamışız. Parametre olarak hem kullanıcıyı hem de index değerini ( map ile gelen ) alıyoruz. 

Buradaki amaç düzenle butonuna tıklandığında ilgili satırın her sütunu için bir input açtırmak. Derdimiz bu. 

İşe ilgili state değerlerini tanımlayarak girelim. 

let [nameEdit, setNameEdit] = useState('')
let [emailEdit, setEmailEdit] = useState('')
let [usernameEdit, setUsernameEdit] = useState('')
let [phoneEdit, setPhoneEdit] = useState('')
let [editClick, setEditClick] = useState(false)
let [userIndex, setUserIndex] = useState(-1)

Ardından düzenle butonuna basıldığı anda şu user bilgilerini yukarıda oluşturduğumuz state değerlerine atayalım. 

let handleEditClick = (user, index) => {

    setNameEdit(user.name)
    setEmailEdit(user.email)
    setUsernameEdit(user.username)
    setPhoneEdit(user.phone)
    setEditClick(true)
    setUserIndex(index)

}

Dikkat ettiyseniz duzenle butonuna basılıp basılmadığını anlamak için editClick adında bir state oluşturduk.

Bu değer true dönüyorsa ilgili satırın her sütunu içine bir input atalım ve içerisine de az önce oluşturduğumuz state leri bağlayalım.

Ad Soyad sütunu için şu şekilde olacak.

<td>
    {/* editClick değeri true ise ve tıklanılan index ile fonksiyona gönerdiğimiz 
     index aynı ise input açılsın, aksi halde ad soyad değeri gösterisin */}

	{editClick && userIndex === index ? (
	
	  <input
		type="text"
		className="form-control"
		value={usernameEdit}
		onChange={(e) => setUsernameEdit(e.target.value)}
	  />
	  
	) : (
	
	  user.username
	  
	)}
	
</td>

Düzenle butonuna basıldığında da başka bir buton gözüksün. Bu da kaydet butonu olsun. Kendileri aşağıdaki gibi olacaktır. 

<td className="d-flex justify-content-between">

    {/* editClick true ise ve userIndex index değeri ile aynı ise
       kaydet butonu gözüksün aksi halde düzenle butonu gözüksün */}
	{editClick && userIndex === index ? (

	 {/* Güncelle Butonu */}
	  <button
		className="btn btn-sm btn-success text-white"
		onClick={(e) => {
			  e.preventDefault()
			  handleUpdate(user)
		}}
	  >
		<i className="fas fa-check"></i>
	  </button>
	
	) : (

	  {/* Düzenle Butonu */}
	  <button
		className="btn btn-sm btn-warning "
		onClick={() => handleEditClick(user, index)}
	  >
		<i className="fas fa-user-edit"></i>
	  </button>
	)}
	
    {/* Sil Butonu */}
	<button
	  className="btn btn-sm btn-danger"
	  onClick={() => handleDelete(user.id)}
	>
	  <i className="fas fa-user-times"></i>
	</button>
	
</td>

Tamamı ise aşağıdadır.

<tbody>
  {usersState.data.map((user, index) => (
	<tr key={index}>
	  <th scope="row">{index + 1}</th>
	  <td>
		{editClick && userIndex === index ? (
		  <input
			type="text"
			className="form-control"
			value={nameEdit}
			onChange={(e) => setNameEdit(e.target.value)}
		  />
		) : (
		  user.name
		)}
	  </td>
	  <td>
		{editClick && userIndex === index ? (
		  <input
			type="text"
			className="form-control"
			value={usernameEdit}
			onChange={(e) => setUsernameEdit(e.target.value)}
		  />
		) : (
		  user.username
		)}
	  </td>
	  <td>
		{editClick && userIndex === index ? (
		  <input
			type="text"
			className="form-control"
			value={emailEdit}
			onChange={(e) => setEmailEdit(e.target.value)}
		  />
		) : (
		  user.email
		)}
	  </td>
	  <td>
		{editClick && userIndex === index ? (
		  <input
			type="text"
			className="form-control"
			value={phoneEdit}
			onChange={(e) => setPhoneEdit(e.target.value)}
		  />
		) : (
		  user.phone
		)}
	  </td>
	  <td className="d-flex justify-content-between">
		{editClick && userIndex === index ? (

          {/* Güncelle Butonu */}
		  <button
			className="btn btn-sm btn-success text-white"
			onClick={(e) => {
			  e.preventDefault()
			  handleUpdate(user)
			}}
		  >
			<i className="fas fa-check"></i>
		  </button>
		) : (

          {/* Düzenle Butonu */}
		  <button
			className="btn btn-sm btn-warning "
			onClick={() => handleEditClick(user, index)}
		  >
			<i className="fas fa-user-edit"></i>
		  </button>
		)}
   
        {/* Sil Butonu */}
		<button
		  className="btn btn-sm btn-danger"
		  onClick={() => handleDelete(user.id)}
		>
		  <i className="fas fa-user-times"></i>
		</button>
	  </td>
	</tr>
  ))}
</tbody>

Her şey doğru gittiyse düzenle butonuna tıkladığınızda şuna benzer bir şey gelmeli. 

Tüm inputlara yeni değerler yazılırken kendilerine ait state değerleri de güncelleniyor. Bunu da onChange() event inden anlayabilirsiniz. 

Hadi günceleme işlemini yapıverelim. 

Dikkat ettiyseniz handleUpdate() adında bir fonksiyonumuz var. Yeşil renkli olan diyelim. 

Şöyle bir fonksiyonumuz olacak.

let handleUpdate = (user) => {
    
	// editClick state ini false yapıyoruz.
	setEditClick(false)
	
	// edit statelerinden gelen değerleri kullanarak yeni bir obje oluşturuyoruz.
	// sol taraftaki değerler redux-database.json dan gelen sabit olan key değerleridir.
	// Biz bu değerlere, onChange() ile yakaladığımız state değerlerini atıyoruz.
	let updatedUser = {
	  name: nameEdit,
	  email: emailEdit,
	  username: usernameEdit,
	  phone: phoneEdit,
	  id: user.id,
	}
	
	// actions altındaki updateUser adlı fonksiyonumuza gönderiyoruz.
	dispatch(actions.updateUser(updatedUser))
}

Her şeyi doğru yaptıysanız sorun yok demektir. Yok bir problem oldu diyorsanız Users.jsx dosyasının tamamını ilgili github sayfasından ya da buraya tıklayarak  bakabilirsiniz.  

 

Yine çok uzadı. Bir sonraki yazıda yeni kullanıcı eklemeyi göreceğiz. Buraya tıklayarak devam edebilirsiniz.

1149 Görüntülenme

Yorum Yap