2 Mayıs 2020 Cumartesi

CONTEXT API NEDIR - REACT REDUX GIRIS

Bu yazimdaki daha once baslamis oldugum React derslerindeki ornekler uzerinden devam edecegim.
Simdi ise daha once state kullanarak yapmis oldugumuz ornekleri Context API kullanarak yapmayi gorecegiz. Onceki yazima bu linkten ulasabilirsiniz.

Uygulamanin son halini ise githubdan gorebilirsiniz : https://github.com/geallen/react_projects

En son ne yaptigimizi bir hatirlayalim.

App icinde bir celebriys array i olusturmustuk. Ve Celebrity icinde bunu kullanmak icin tasiyorduk. Daha sonra herbir celebrity i ekranda gosteriyorduk. Yani datayi componentlerde kullanmak icin tek tek gezdiriyorduk. Bu yontem hic efektif degil ve buyuk uygulamalarda tercih edilmiyor.

Bunun icin Context Api yapisini kullanacagiz. Celebritys datasini ortak bir yerde olusturup tum componentlerde ona erismek daha uygun bir yontem. Bunun icin Provider ve Consumer yapisina ihtiyacimiz var. Oncelikle Celebritys i bize provide etmeli. Biz de onu consume etmeli ve ekranda gosterebilmeliyiz.

Bunun icin oncelikle context.js adinda bir dosya olusturuyoruz. Contextimizi burada olusturacagiz.

Contextin icinde datalarimizi setliyoruz ve provider icinde gnderiyoruz, Consumer ile de export ediyoruz ki baskalari kullanabilsin.

import React, { Component } from 'react'

const CelebrityContext = React.createContext();

export class CelebrityProvider extends Component {
    state = {
        users : [
            {
              id: 1,
              name : "Monica Geller",
              movie : "Friends",
              age : "28"
            },
            {
              id: 2,              
              name : "John Locke",
              movie : "Lost",
             age : "43"
            },
            {
              id: 3,
              name : "Ted Mosby",
              movie : "HIMYM",
              age : "35"
            }
          ]
    }
   render() {
        return (
            <div>
                <CelebrityContext.Provider value= {this.state}>
                   {this.props.children}
              </CelebrityContext.Provider>
            </div>
        )
    }
}
const CelebrityConsumer = CelebrityContext.Consumer;
export default CelebrityConsumer;

En sonda da contextten Celebrityconsumer olusturmaliyiz ki bunu kullanabilelim.

Daha sonra index.js e gecip App componentimizi CelebrityProvider ile saralim ki bu provider App in her yerinden erisilebilsin.

import {CelebrityProvider} from './context';
ReactDOM.render(
  <React.StrictMode>
    <CelebrityProvider>
      <App />
    </CelebrityProvider>,
 </React.StrictMode>,
  document.getElementById('root')
);


Bu sekilde datalara heryerden erisebilecegiz.

App.js icinde Celebritys componentimizi olusturduk ekranda gosterebilmek icin. Simdi Celebritys.js icine gecelim ve Consumer dan aldigimiz datalari ekranda gostermeye calisalim.

Bunun icin Consumer componentini olusturuyoruz ve consumerdan gelen value'yu(this.state icinde users arrayi vardi) user a atiyoruz.

import React, { Component } from 'react'
import Celebrity from './celebrity';
import CelebrityConsumer from '../context';

export default class celebritys extends React.Component {
    render() {
        return(
            <CelebrityConsumer>
                {
                    value => {
                        const {users} = value;
                        console.log("users"+ users);
                        return (
                            <div>
                                {
                                    users.map(celebrity => {
                                        return (
                                            <Celebrity 
                                             key = {celebrity.id}
                                             id = {celebrity.id}
                                             name = {celebrity.name}
                                             movie = {celebrity.movie}
                                                age = {celebrity.age} />
                                        )
                                    })
                                }
                            </div>
                        )
                    }
                }
            </CelebrityConsumer>
        )        
    }
}

Simdi ise contextapi dar yer alan dispatcher / reducer / acion kavramlarini gorelim.

Oncelikle projemizin son haline bakalim. Ekranda asagidaki gibi celebriyleri siraliyorduk ve isme tiklandiginda item'in detayini gosteriyorduk.Simdi ise yanlarinda yer alan cop kutusuna basildiginda her bir celebrity'i silecegiz ve bunu dispacther ve reducer kullanarak yapacagiz.



Oncelikle yazmis oldugumuz contexte gidelim ve contetimizin state'ine dispatcher ekleyelim.

dispatch : action => {
            this.setState(state => reducer(state, action))
          }

Bu koddan anlamamiz gereken su: dispatch tetiklendiginde bir action parametresi gonderilecek ve biz state'i guncelleyecegiz. Nasil guncelleyecegiz ? reducer fonksiyonunu cagirarak. Reducer ise state ve action parametreleri alip state i guncelleyecek. Simdi reducer i tanimlayalim. Yine context icinde tabiki.

const reducer = (state, action) => {
  switch(action.type){
    case "DELETE_USER":
      return {
        ...state,
        users: state.users.filter(user => action.payload != user.id)
      }
    default:
      return state
  }
}

Evet dedigimiz gibi gelen action tipi Delete_user ise state icindeki users'i guncelleyecegiz. Nasil ?

Koda baktigimizda action icinde iki parametre gonderilmesine ihtiyacimiz oldugunu goruyoruz. Type ve payload olmak uzere. Simdi users'i nasil guncelleyecegimize gelirsek payload olarak gonderilen id haric diger idler users da kalacak. Yani sadece payload icinde gonderilen id silinecek.

Simdi de celebrity componentimize gelelim ve bu parametreleri cop kutusuna tiklaninca gonderelim.

<i className="far fa-trash-alt"  onClick={this.deleteUserOnClick.bind(this, dispatch)} 
 style={{cursor:"pointer"}}></i>


Burada gordugmuz gibi fonksiyona dispacth i gonderdik. Simdi de fonksiyon tanimina bakalim.

deleteUserOnClick = (dispatch, e) =>{
        const {id} = this.props;
        dispatch({type : "DELETE_USER", payload :id})
    }

Yani dispatch i cagir diyoruz. Dispatch icin action parametresine ihtiyacimiz vardi. Onu da gondermis olduk.

Bu sekilde calistirdigimizda itemlerimiz silinecek.


DYNAMIC CSS 

Ekranda yer alan card itemlerimize tiklandiginda acilinca o cardlarimizin background color larini degistirmeye calisalim. Acilip kapanmasini state icndeki isVisible ile yapiyorduk. O halde burada da onu kullanabiliriz.


<div className="card" style={isVisible ? {backgroundColor: '#B0BEC5', 
    color: 'white'} : null}>
  <div className="card-header d-flex justify-content-between">
  <h4 className="d-inline" onClick= {this.onClickName}>{name}</h4>
  <i className="far fa-trash-alt" 
onClick= {this.deleteUserOnClick.bind(this, dispatch)}  
style={{cursor:"pointer"}}></i>
           </div>
             { isVisible ? 
            <div className="card-body">
                <p className="card-text">Movie : {movie}</p>
                <p className="card-text">Age : {age}</p>
            </div>
            : null}
            </div> 


ADDING OBJECT TO STATE LIST

En son ornegimizde celebrity detaylari gosterme ve silme islemlerini yapmistik. Simdi de bir form olusturup yeni bir celebrity ekleme islemlerini yapalim.

import React, { Component } from 'react'
import posed from 'react-pose';
import CelebrityConsumer from '../context';

var uniqid = require('uniqid');
const Animation = posed.div({
    visible : {
        opacity : 1,
        applyAtStart : {
            display : "block"
        }
    },
    hidden : {
        opacity : 0,
        applyAtEnd : {
            display : "none"
        }
    }
});

export default class addCelebrity extends Component {
    state = {
        visibility : true,
        name : "",
        movie : "",
        age : ""
    }

    changeVisibility = (e) => {
        this.setState({ visibility : !this.state.visibility})
    }

    changeInput = (e) => {
        this.setState({
            [e.target.name: e.target.value
        })
    }

    addCelebrity= (dispatch, e) => {
        e.preventDefault();
        const {name, movie, age} = this.state;
        const newCelebrity = {
            id : uniqid(),
            name,
            movie,
            age 
        }
        dispatch({type : "ADD_USER", payload : newCelebrity});
    }

    render() {
        const {visibility, name, movie, age} = this.state;

        return(
            <CelebrityConsumer>
                {
                    value => {
                        const {dispatch} = value;
                        return (
                          <div className="col-md-8 mb-4">
                 <button className="btn btn-dark btn-block mb-2" 
onClick={this.changeVisibility} >
            {visibility ? 'Hide Form' : 'Show Form'}</button>
<Animation pose={this.state.visibility ?
    'visible' : 'hidden'}>
<div className="card">
<div className="card-header">
<h4>Add Celebrity Form</h4>
 </div>
<div className="card-body">
<form onSubmit = {this.addCelebrity.bind(this,dispatch)}>
<div className="form-group">
    <label htmlFor="name">Name</label>
    <input type="text" name="name" id="id" 
    placeholder="Enter Name" className="form-control"
    value={name} onChange={this.changeInput}></input>
</div>
<div className="form-group">
    <label htmlFor="name">Movie</label>
    <input type="text" name="movie" id="movie" 
    placeholder="Enter Movie" className="form-control"
    value={movie} onChange={this.changeInput}></input>
 </div>
<div className="form-group">
    <label htmlFor="name">Age</label>
    <input type="text" name="age" id="age" 
    placeholder="Enter Age" className="form-control"
     value={age} onChange={this.changeInput}></input>
</div>
<button type="submit" className="btn btn-danger btn-block">
Add Celebrity</button>
</form>
 </div>
 </div>
</Animation>
</div>
 )
}
}
</CelebrityConsumer>
        )        
    }
}


Bu kodun kisaca uzerinden gecelim. Visibility visible ise "Add Celebrity Form " isminde bir form gorunecek. Bu formun animasyon olarak grunup kapanmasi icin Animation componentini kullandik. react-pse dan import ettik. Detaylar icin su linke bakabilirsiniz.

Form icerisinde 3 tane input alani var : Name, Movie, Age olmak uzere.

Bu olusturdugumuz inputlara bir sey yazilamiyor ilk basta. Yazilmasini saglamak icin onChange eventi ekliyoruz her bir input fieldina. 

onChange icin changeInput adinda bir fonksiyon yazdik ve icinde naptigimiza bakalim:

changeInput = (e) => {
        this.setState({
            [e.target.name: e.target.value
        })
    }

Gelen inputlarin namelerinden valuelarini alabiliriz artik bu sekilde.

Form submit edildiginde ise yazdigimiz fonksiyon addCelebrity adinda. Burada aldigimiz dispacth i bind ettik. Simdi de addCelebrity icine bakalim.

addCelebrity= (dispatch, e) => {
        e.preventDefault();
        const {name, movie, age} = this.state;
        const newCelebrity = {
            id : uniqid(),
            name,
            movie,
            age 
        }
        dispatch({type : "ADD_USER", payload : newCelebrity});
    }

e.preventDefault ile form submit edildiginde sayfanin yenilenmesini onluyoruz. 

Yeni bir celebrity olustrduk. Bunu ekleyecegiz. Her yeni celebrity nin id si farkli olmasi icin unique id atamaliyiz.  Bunun icin de uniquie id generate eden bir framework kullanacagiz. 

Su sekilde olusturduk. 
var uniqid = require('uniqid');


Kullanimi icin su linke bakabilirsiniz.

Daha sonra da dispatch icinde bir type ve bir de payload adinda parametre gonderdik.

dispatch({type : "ADD_USER", payload : newCelebrity});

Reducer icinde dispatch'den gelen type'a bakacagiz. Eger ADD_USER ise payload icindeki user'i users listesine ekleyecegiz. Simdi de context icindeki reducer'e gecelim...

const reducer = (state, action) => {
  switch(action.type){
    case "DELETE_USER":
      return {
        ...state,
        users: state.users.filter(user => action.payload !== user.id)
      }
    case "ADD_USER":
        return {
          ...state,
          users: [...state.users, action.payload]
      }    
    default:
      return state
  }
}


ADD_USER ise su sekilde yapmisiz.

...state demek var olan state'in aynisini al demektir. Daha sonra da state icindeki users'i gelen user'i ekleyerek guncellemisiz. Bu durumda users listesi guncellendigi icin yeniden render edilecek ve ekledigimiz celebrity de ekranda gorunecektir.

Add Celebrity Form un gorunumu su sekildedir.


UPDATE OBJECT IN STATE LIST

Celebrity Guncelleme isine bakalim simdi de . Bunun icin sil butonunun yanina bir icon daha ekledim guncellemek icin. Guncelle dedigimde yazdigim fonksiyona bakalim simdi de .

 updateCelebrity = (dispatch, e) => {
        e.preventDefault();
        const {movie, age} = this.state;
        const {name, id} = this.props;
        const celebrity = {
            id,
            name,
            movie,
            age
        }

        dispatch({type: "UPDATE_USER", payload: celebrity})
    }

Simdi de reducer icine bakalim. 

const reducer = (state, action) => {
  switch(action.type){
    case "DELETE_USER":
      return {
        ...state,
        users: state.users.filter(user => action.payload !== user.id)
      }
    case "ADD_USER":
        return {
          ...state,
          users: [...state.users, action.payload]
      }
    case "UPDATE_USER":
let index = action.payload.id;
state.users.find(user => user.id === index).age = action.payload.age;
state.users.find(user => user.id === index).movie=action.payload.movie;

      return {
        ...state
      }
    default:
      return state
  }
}

UPDATE_USER geldiginde state'in user'indan gelen id'li useri buluyorum ve bu user'in age ve movie degerlerini gonderdigim inputtaki degerle guncelliyorum. Daha sonrada state'i return ediyorum.

Projenin son haline github hesabimdan ulasabilirsiniz: https://github.com/geallen/react_projects

Hiç yorum yok:

Yorum Gönder