Globalni ili “application state” je tema kojom se bavimo danas. U principu stvar funkcionira slično kao built-in React “useContext” hook.

Cijeli projekt se nalazi ovdje: LINK

Ako želimo jednostavno opisati state, možemo reći da se “state” sastoji od varijable i metode. Varijabla je trenutna vrijednost a metoda je funkcija koja postavlja tu trenutnu vrijednost. Ako se varijabla promjeni, komponente aplikacije se prilagođavaju novom state-u. Osim toga, imamo state inicijaciju, odnosno početne (init) vrijednosti state varijabli aplikacije.

Prije nego što nastavimo, ne bi bilo loše da bacimo oko na ovu objavu.

Pravimo projekt:

npx create-react-app moja-redux-aplikacija

Instaliramo Redux:

npm install --save redux react-redux
npm install @reduxjs/toolkit

…pa brišemo sve u “src” mapi osim:

index.js
App.js  

Zbog jednostavnosti, dodajmo “školski” w3.css framework u “public/index.html” zaglavlje.

Komponente

Pravimo datoteku “/src/comps/header/header.js” sa sadržajem:

const Header = () => {
  return (
	<div className="w3-bar w3-green">
	  <div className="w3-bar-item w3-black">Home</div>
	  <div className="w3-bar-item">Posts</div>
	  <div className="w3-bar-item">About</div>
	</div> 
  );
}
export default Header;

Pravimo datoteku “/src/comps/body/body.js” sa sadržajem:

const Body = () => {		
  return (    
  <div className="w3-container">
	<div className="w3-panel w3-blue">
	  <h3>Information!</h3>
	  <p>Hello</p>
	</div>  
  </div>    
  );
}
export default Body;

Zatim je uvozimo u App.js:

import Header from './comps/header/header.js';
import Body from './comps/body/body.js';

const App = () => {
  return (
    <div className="App">
     <Header />
     <Body />
    </div>
  );
}
export default App;

Slice

Slice je dio state-a dodijeljen komponenti. Ime slice datoteke je naziv komponente i “Slice” sufiks. Što bi u našem primjeru bilo “HeaderSlice”, odnosno “/src/comps/header/headerSlice.js”.

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  currentHeader: 'home',
};

export const headerSlice = createSlice({
  name: "header",
  initialState,
  reducers: {
  },
});

export const {  } = headerSlice.actions;
export default headerSlice.reducer;

E sad, tu imamo “initialState” gdje postavljamo state varijablu “currentHeader” sa vrijednosti “home”. Ova je “default” postavka koja je dodijeljena varijabli pri pokretanju aplikacije.

Reducers, u biti, to su funkcije koje uzimaju neku state varijablu, mijenjaju ju i vračaju/postave novu vrijednost. Tako ćemo mi napraviti “setCurrentHeader” funkciju koja će nam postaviti novi “currentHeader”. Nakon toga, moramo eksportirati reducer.

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  selectedItem: 'home',
  changedTimes: 0,
};

export const headerSlice = createSlice({
  name: "header",
  initialState,
  reducers: {
    setCurrentHeader: (state, action) => {
      state.selectedItem = action.payload;
    },
    countChange: (state) => {
      state.changedTimes += 1;
    },
  },
});

export const { setCurrentHeader, countChange } = headerSlice.actions;
export default headerSlice.reducer;

Store

Store je kontejner koji sadrži state aplikacije. Zato ćemo napraviti “src/store.js” datoteku sa sadržajem:

import { configureStore } from "@reduxjs/toolkit";
import headerReducer from "./comps/header/headerSlice.js";

export const store = configureStore({
  reducer: {
    header: headerReducer,
  },
});

Povratak Header komponenti

Sada bi trebali malo poraditi Header komponenti. Ono što želimo je da kada kliknemo na menu item, da item bude označen.

import { useState } from "react";

const Header = () => {

	const barItems = ['Home','Posts','About'];
	const [activePage, setActivePage] = useState('home');

	const createBarItem = (name, activeName) => {	
		const key = name +'_'+ Date.now();
		const nameLowerCase = name.toLowerCase();					
		const classString = (activeName == nameLowerCase) ? "w3-bar-item w3-black" : "w3-bar-item"; 
		return ( <div onClick={() => setActivePage(nameLowerCase)} key={key} className={classString} >{name}</div> );
	};	
	
	return (
		<div className="w3-bar w3-green">
			{barItems.map((barName) => (
				createBarItem(barName, activePage)
			))}		
		</div> 
	);	
}

export default Header;

Body state

U Body komponenti želimo znati koji je izabrani item u Header komponenti pa ga zatim prikazati u paragrafu. Ovdje izvlačimo “selectedItem” vrijednost koja je pohranjena u “application state”.

import { useSelector} from "react-redux";

const Body = () => {
	
  const { selectedItem, changedTimes } = useSelector((state) => state.header);

  return (
        
  <div className="w3-container">
	<div className="w3-panel w3-blue">
	  <h3>Information!</h3>
	  <p>Selected item: {selectedItem}</p>
	  <p>Count change: {changedTimes}</p>
	</div>  
  </div>  
  
  );
}
export default Body;

Povezivanje Header komponente sa “application state”

Trenutno izabrani header item je pohranjen u “activePage” state varijabli. Da ne pišemo sad sve iznova, koristimo “useEffect” hook. Tako da kada se promjeni “activePage” vrijednost, pozivamo “setCurrentHeader” reducer i unosimo vrijednost koja je sada pohranjena u “application state”.

import { useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import { setCurrentHeader, countChange } from "./headerSlice";
import './header.css';

const Header = () => {

	const barItems = ['Home','Posts','About'];
	const [activePage, setActivePage] = useState('home');

	const createBarItem = (name, activeName) => {	
		const key = name +'_'+ Date.now();
		const nameLowerCase = name.toLowerCase();					
		const classString = (activeName == nameLowerCase) ? "w3-bar-item w3-finger w3-black" : "w3-finger w3-bar-item"; 
		return ( <div onClick={() => setActivePage(nameLowerCase)} key={key} className={classString} >{name}</div> );
	};	
	
	
	const dispatch = useDispatch();
	useEffect(() => {		
		dispatch(setCurrentHeader(activePage));		
		dispatch(countChange());
	}, [activePage]);
	
	
	return (
		<div className="w3-bar w3-green">
			{barItems.map((barName) => (
				createBarItem(barName, activePage)
			))}		
		</div> 
	);
	
}

export default Header;