A Redux használata a ReactJS-ben valós példákkal

Amióta elkezdtem dolgozni a ReactJS-szel, a Creative-Timnél, csak egyszerű reakcióalkalmazások vagy sablonok létrehozására használtam, ha akarja. A ReactJS-t csak a create-reago-app alkalmazással használtam, és soha nem próbáltam integrálni valami mással.

Sok felhasználó megkérdezte tőlem vagy a csapatomtól, hogy az általam létrehozott sablonok tartalmaznak-e Redux-ot. Vagy ha olyan módon lettek létrehozva, hogy a Redux-szal együtt használhatók legyenek. A válaszom mindig olyasmi volt, hogy: „Még nem dolgoztam Reduxszal, és nem tudom, milyen választ kell adnom neked”.

Tehát itt vagyok, írok egy cikket a Redux-ról és arról, hogyan kell használni a React-ben. Később ebben a cikkben hozzáadom Reduxot az egyik projekthez, amelyet az elmúlt és néhány évben dolgoztam.

Jó tudni, mielőtt nekivágnánk ennek a két könyvtárnak:

  • A [email protected] fájlt fogom használni (globálisan telepítve)
  • Az [email protected] fájlt használom
  • A Node.js verzióm a bejegyzés írásakor 10.13.0 (LTS) volt
  • Ha inkább a Webpack alkalmazást szeretné használni, akkor elolvashatja a Webpack cikkemet, és összekapcsolhatja azt, amit itt megmutatok Önnek, és azt, amit itt megmutatok.

Új ReactJS alapú projekt létrehozása és Redux hozzáadása hozzá

Először hozzunk létre egy új reakció alkalmazást, CD-t és indítsuk el.

create-react-app react-redux-tutorial cd react-redux-tutorial npm start

Mint láthatjuk, a create-reagál-alkalmazás egy nagyon alapsablont ad nekünk egy bekezdéssel, egy horgonyt a React webhelyhez és a hivatalos ReactJS ikonhoz.

Nem mondtam el nektek, mire fogjuk használni a Redux-et, vagy mit csinálunk itt. És ez azért van, mert szükségem volt a fenti gif képre.

Annak érdekében, hogy ez az oktató cikk könnyű és könnyen érthető legyen, nem fogunk valami nagyon összetettet felépíteni. A Redux-ot arra használjuk, hogy a fenti React kép leálljon vagy elkezdjen forogni.

Tehát folytatva tegyük hozzá a következő Redux csomagokat:

npm install --save redux react-redux

redux v4.0.1

  • A Redux nagyon általános értelemben az, hogy globális állapotot hoz létre az egész alkalmazás számára, amelyhez bármelyik komponens hozzáférhet
  • Ez egy állami menedzsment könyvtár
  • Csak egy állapota van az egész alkalmazáshoz, az egyes komponensekhez nem

react-redux v5.1.1

  • Ezt arra használják, hogy hozzáférhessünk a Redux adataihoz és módosítsuk azokat úgy, hogy műveleteket küldünk a Redux-nek - valójában nem a Redux-nek, de eljutunk oda
  • A hivatalos dokumentumok állapota: Lehetővé teszi, hogy a React összetevői beolvassák az adatokat a Redux áruházból, és műveleteket küldjenek a boltba az adatok frissítéséhez.

MEGJEGYZÉS : Ha problémái vannak a fenti paranccsal, próbálja meg külön telepíteni a csomagokat

Amikor a Reduxszel dolgozik, három fő dologra lesz szüksége:

  • műveletek: ezek olyan objektumok, amelyeknek két tulajdonsággal kell rendelkezniük, az egyik leírja a művelet típusát, a másik pedig azt, hogy mit kell megváltoztatni az alkalmazás állapotában.
  • reduktorok: ezek a funkciók valósítják meg a cselekvések viselkedését. Megváltoztatják az alkalmazás állapotát a műveletleírás és az állapotváltozás leírása alapján.
  • store: összehozza a műveleteket és a reduktorokat, megtartva és megváltoztatva az egész alkalmazás állapotát - csak egy üzlet van.

Ahogy fentebb mondtam, megállunk és elkezdjük pörgetni a React logót. Ez azt jelenti, hogy az alábbiak szerint két cselekvésre lesz szükségünk:

1 - Linux / Mac parancsok

mkdir src/actions touch src/actions/startAction.js touch src/actions/stopAction.js

2 - Windows parancsok

mkdir src\actions echo "" > src\actions\startAction.js echo "" > src\actions\stopAction.js

Most szerkesszük az src / actions / startAction.js fájlt az alábbiak szerint:

export const startAction = { type: "rotate", payload: true };

Tehát azt mondjuk reduktorunknak, hogy a művelet típusa a React logó forgatására ( forgatására ) vonatkozik. És a React logó forgatásának állapotát igazra kell változtatni - azt akarjuk, hogy a logó elkezdjen forogni.

Most szerkesszük az src / actions / stopAction.js fájlt az alábbiak szerint:

export const stopAction = { type: "rotate", payload: false };

Tehát azt mondjuk reduktorunknak, hogy a művelet típusa a React logó forgatására ( forgatására ) vonatkozik. A React logó forgatásának állapotát pedig hamisra kell változtatni - azt akarjuk, hogy a logó ne forogjon tovább .

Hozzuk létre az alkalmazás szűkítőjét is:

1 - Linux / Mac parancsok

mkdir src/reducers touch src/reducers/rotateReducer.js

2 - Windows parancsok

mkdir src\reducers echo "" > src\reducers\rotateReducer.js

És adja hozzá a következő kódot:

export default (state, action) => { switch (action.type) { case "rotate": return { rotating: action.payload }; default: return state; } };

Tehát a reduktor mindkét tevékenységünket megkapja, mindkettő forog, és mindkettő ugyanazt az állapotot változtatja meg az alkalmazásban - ami state.rotating . Ezen műveletek hasznos terhe alapján az state.rotating igaz vagy hamis értékre változik .

Hozzáadtam egy alapértelmezett esetet, amely megváltoztatja az állapotot, ha a művelettípus nem forog . Az alapértelmezett érték abban az esetben van, ha létrehozunk egy műveletet, és elfelejtjük hozzáadni az esethez a kis- és nagybetűt. Így nem töröljük az alkalmazás teljes állapotát - egyszerűen nem csinálunk semmit, és megtartjuk azt, ami volt.

Az utolsó dolog, amit meg kell tennünk, hogy létrehozzuk az áruházunkat az egész alkalmazás számára. Mivel az egész alkalmazásnak csak egy üzlete van / egy állapota, nem fogunk új mappát létrehozni az áruház számára. Ha akar, létrehozhat egy új mappát az áruház számára, és hozzáadhatja azt, de ez nem olyan, mint például a műveleteknél, ahol több műveletet is elvégezhet, és jobb, ha azokat egy mappában tartja.

Tehát ennek ellenére ezt a parancsot fogjuk futtatni:

1 - Linux / Mac parancs

touch src/store.js

2 - Windows parancs

echo "" > src\store.js

És adja hozzá a következő kódot is:

import { createStore } from "redux"; import rotateReducer from "reducers/rotateReducer"; function configureStore(state = { rotating: true }) { return createStore(rotateReducer,state); } export default configureStore;

Tehát létrehozunk egy configureStore nevű függvényt , amelyben alapértelmezett állapotot küldünk, és a létrehozott reduktort és az alapértelmezett állapotot használva létrehozzuk az áruházunkat.

Nem vagyok biztos benne, hogy látta-e az importálásomat, abszolút utakat használnak, ezért lehet, hogy ebből kifolyólag hibái vannak. Ennek megoldása a kettő egyike:

Bármelyik

1 - Adjon hozzá egy .env fájlt az alkalmazásához, így:

echo "NODE_PATH=./src" > .env

Vagy

2 - Telepítse a cross-env-t globálisan, és változtassa meg az indító szkriptet a package.json fájlból:

npm install -g cross-env

És a csomag belsejében.json

"start": "NODE_PATH=./src react-scripts start",

Most, hogy beállítottuk áruházunkat, tevékenységeinket és reduktorunkat, új osztályt kell felvennünk az src / App.css fájlba. Ez az osztály szünetelteti a logó forgó animációját.

Tehát a következőket fogjuk írni az src / App.css fájlba :

.App-logo-paused { animation-play-state: paused; }

Tehát az App.css fájlnak ilyennek kell kinéznie:

.App { text-align: center; } .App-logo { animation: App-logo-spin infinite 20s linear; height: 40vmin; } /* new class here */ .App-logo-paused { animation-play-state: paused; } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .App-link { color: #61dafb; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

Most csak az src / App.js fájlt kell módosítanunk , hogy az hallgassa az üzlet állapotát. Amikor a logóra kattint, az elindítási vagy leállítási műveletek egyikét hívja meg.

Először is össze kell kapcsolnunk az összetevőt a redux tárolóval, így importáljuk a connect-et a reakció-reduxból .

import { connect } from "react-redux";

Ezt követően exportáljuk az App komponensünket a connect módszer segítségével:

export default connect()(App);

A redux bolt állapotának megváltoztatásához szükségünk lesz a korábban elvégzett műveletekre, ezért importáljuk őket is:

import { startAction } from "actions/startAction"; import { stopAction } from "actions/stopAction";

Most be kell szereznünk az állapotot a boltunkból, és azt kell mondanunk, hogy azt akarjuk, hogy a start és a stop műveleteket használják az állapot megváltoztatására.

Ez a connect funkcióval történik, amely két paramétert fogad el:

  • mapStateToProps: this is used to retrieve the store state
  • mapDispatchToProps: this is used to retrieve the actions and dispatch them to the store

You can read more about them here: react-redux connect function arguments.

So let’s write inside our App.js (at the end of the file if you may):

const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ startAction: () => dispatch(startAction), stopAction: () => dispatch(stopAction) });

After this, let’s add them inside our connect function like so:

export default connect(mapStateToProps, mapDispatchToProps)(App);

And right now, inside our App component, we can access the store state, the startAction and stopAction through props.

Let’s change the img tag to:

So, what we are saying here is, if the store state of rotating (this.props.rotating) is true, then we want just the App-logoclassName to be set to our img. If that is false, then we also want the App-logo-paused class to be set in the className. This way we pause the animation.

Also, if this.props.rotating is true, then we want to send to our store for the onClick function and change it back to false, and vice-versa.

We are almost done, but we’ve forgot something.

We haven’t yet told our react app that we have a global state, or if you will, that we use redux state management.

For this, we go inside src/index.js, we import a Provider from react-redux, and the newly created store like so:

import { Provider } from "react-redux"; import configureStore from "store";
  • Provider: makes the Redux store available to any nested components that have been wrapped in the connect function

After this, instead of rendering our App component directly, we render it through our Provider using the store that we’ve created like so:

ReactDOM.render(   , document.getElementById('root') );

Here we could have used the configureStore function with some other state, for example configureStore({ rotating: false }).

So, your index.js should look like this:

import React from 'react'; import ReactDOM from 'react-dom'; // new imports start import { Provider } from "react-redux"; import configureStore from "store"; // new imports stop import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; // changed the render ReactDOM.render(   , document.getElementById('root') ); // changed the render serviceWorker.unregister();

Let’s go ahead and see if our redux app works:

Using action creators

Optionally, instead of actions, we can use action creators, which are functions that create actions.

This way, we can combine our two actions in just one function and reduce a bit our code.

So, let’s go ahead and create a new file:

1 — Linux / Mac command

touch src/actions/rotateAction.js

2 — Windows command

echo "" > src\actions\rotateAction.js

And add this code:

const rotateAction = (payload) => { return { type: "rotate", payload } } export default rotateAction;

We are going to send an action of type rotate, with a payload that we are going to get in the App component.

Inside the src/App.js component, we need to import our new action creator:

import rotateAction from "actions/rotateAction";

Add the new function to the mapDispatchToProps like so:

rotateAction: will receive a (payload) and will dispatch the rotateAction with the payload

Change the onClick function to:

onClick={() => this.props.rotateAction(!this.props.rotating)}

And finally, add our new action creator to the mapDispatchToProps like this:

rotateAction: (payload) => dispatch(rotateAction(payload))

We can also delete the old imports for the old actions, and delete them from the mapDispatchToProps as well.

This is how you new src/App.js should look like:

import React, { Component } from 'react'; // new lines from here import { connect } from "react-redux"; import rotateAction from "actions/rotateAction"; //// new lines to here import logo from './logo.svg'; import './App.css'; class App extends Component { render() { console.log(this.props); return (  this.props.rotateAction(!this.props.rotating) } />

Edit src/App.js and save to reload.

Learn React ); } } const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ rotateAction: (payload) => dispatch(rotateAction(payload)) }); export default connect(mapStateToProps, mapDispatchToProps)(App);

A real-life example with Paper Dashboard React

As you will see in the above gif image, I am using the right menu to change the colors of the menu on the left. This is achieved by using component states, and by passing that state from a parent component to the two menus and some functions to change that state.

I thought it would be a nice example, to take this product and replace the component states with Redux.

You can get it in these 3 ways:

  1. Download from creative-tim.com
  2. Download from Github
  3. Clone from Github:
git clone //github.com/creativetimofficial/paper-dashboard-react.git

Now that we have this product, let’s cd into it and install again redux and react-redux:

npm install --save redux react-redux

After this, we need to create the actions. Since in the right menu we have 2 colors that set the background of the left menu, and 5 colors that change the color of the links, we need 7 actions, or 2 actions creators — and we are going with this second option since it is a bit less code to write:

1 — Linux / Mac commands

mkdir src/actions touch src/actions/setBgAction.js touch src/actions/setColorAction.js

2 — Windows commands

mkdir src\actions echo "" > src\actions\setBgAction.js echo "" > src\actions\setColorAction.js

After this, let’s create the actions code as follows:

src/actions/setBgAction.js

const setBgAction = (payload) => { return { type: "bgChange", payload } } export default setBgAction;

src/actions/setColorAction.js

const setColorAction = (payload) => { return { type: "colorChange", payload } } export default setColorAction;

Now, as in the first part, we need the reducer:

1 — Linux / Mac commands

mkdir src/reducers touch src/reducers/rootReducer.js

2 — Windows commands

mkdir src\reducers echo "" > src\reducers\rootReducer.js

And the code for the reducer:

export default (state, action) => { switch (action.type) { case "bgChange": return { ...state, bgColor: action.payload }; case "colorChange": return { ...state, activeColor: action.payload }; default: return state; } };

As you can see here, unlike our first example, we want to keep our old state and update its contents.

We also need the store:

1 — Linux / Mac command

touch src/store.js

2 — Windows command

echo "" > src\store.js

The code for it:

import { createStore } from "redux"; import rootReducer from "reducers/rootReducer"; function configureStore(state = { bgColor: "black", activeColor: "info" }) { return createStore(rootReducer,state); } export default configureStore;

Inside the src/index.js we need:

// new imports start import { Provider } from "react-redux"; import configureStore from "store"; // new imports stop

And also, change the render function:

ReactDOM.render(    {indexRoutes.map((prop, key) => { return ; })}   , document.getElementById("root") );

So the index.js file should look like this:

import React from "react"; import ReactDOM from "react-dom"; import { createBrowserHistory } from "history"; import { Router, Route, Switch } from "react-router-dom"; // new imports start import { Provider } from "react-redux"; import configureStore from "store"; // new imports stop import "bootstrap/dist/css/bootstrap.css"; import "assets/scss/paper-dashboard.scss"; import "assets/demo/demo.css"; import indexRoutes from "routes/index.jsx"; const hist = createBrowserHistory(); ReactDOM.render(    {indexRoutes.map((prop, key) => { return ; })}   , document.getElementById("root") );

Now we need to make some changes inside src/layouts/Dashboard/Dashboard.jsx. We need to delete the state and the functions that change the state. So go ahead and delete these bits of code:

The constructor (between lines 16 and 22):

constructor(props){ super(props); this.state = { backgroundColor: "black", activeColor: "info", } }

The state functions (between lines 41 and 46):

handleActiveClick = (color) => { this.setState({ activeColor: color }); } handleBgClick = (color) => { this.setState({ backgroundColor: color }); }

The sidebar bgColor and activeColor props (lines 53 and 54):

bgColor={this.state.backgroundColor} activeColor={this.state.activeColor}

All of the FixedPlugin props (between lines 59–62):

bgColor={this.state.backgroundColor} activeColor={this.state.activeColor} handleActiveClick={this.handleActiveClick} handleBgClick={this.handleBgClick}

So, we remain with this code inside the Dashboard layout component:

import React from "react"; // javascript plugin used to create scrollbars on windows import PerfectScrollbar from "perfect-scrollbar"; import { Route, Switch, Redirect } from "react-router-dom"; import Header from "components/Header/Header.jsx"; import Footer from "components/Footer/Footer.jsx"; import Sidebar from "components/Sidebar/Sidebar.jsx"; import FixedPlugin from "components/FixedPlugin/FixedPlugin.jsx"; import dashboardRoutes from "routes/dashboard.jsx"; var ps; class Dashboard extends React.Component { componentDidMount() { if (navigator.platform.indexOf("Win") > -1) { ps = new PerfectScrollbar(this.refs.mainPanel); document.body.classList.toggle("perfect-scrollbar-on"); } } componentWillUnmount() { if (navigator.platform.indexOf("Win") > -1) { ps.destroy(); document.body.classList.toggle("perfect-scrollbar-on"); } } componentDidUpdate(e) { if (e.history.action === "PUSH") { this.refs.mainPanel.scrollTop = 0; document.scrollingElement.scrollTop = 0; } } render() { return ( {dashboardRoutes.map((prop, key) => { if (prop.pro) { return null; } if (prop.redirect) { return ; } return (  ); })} ); } } export default Dashboard;

We need to connect the Sidebar and FixedPlugin components to to the store.

For src/components/Sidebar/Sidebar.jsx:

import { connect } from "react-redux";

And change the export to:

const mapStateToProps = state => ({ ...state }); export default connect(mapStateToProps)(Sidebar);

For the src/components/FixedPlugin/FixedPlugin.jsx:

import { connect } from "react-redux"; import setBgAction from "actions/setBgAction"; import setColorAction from "actions/setColorAction";

And the export should now be:

const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ setBgAction: (payload) => dispatch(setBgAction(payload)), setColorAction: (payload) => dispatch(setColorAction(payload)) }); export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);

We are going to have these next changes:

  • anywhere you find the word handleBgClick, you’ll need to change it to setBgAction
  • anywhere you find the word handleActiveClick, you’ll need to change it to setColorAction

So, the FixedPlugin component should now look like this:

import React, { Component } from "react"; import { connect } from "react-redux"; import setBgAction from "actions/setBgAction"; import setColorAction from "actions/setColorAction"; import Button from "components/CustomButton/CustomButton.jsx"; class FixedPlugin extends Component { constructor(props) { super(props); this.state = { classes: "dropdown show" }; this.handleClick = this.handleClick.bind(this); } handleClick() { if (this.state.classes === "dropdown") { this.setState({ classes: "dropdown show" }); } else { this.setState({ classes: "dropdown" }); } } render() { return ( 
    
  • SIDEBAR BACKGROUND
  • { this.props.setBgAction("black"); }} /> { this.props.setBgAction("white"); }} />
  • SIDEBAR ACTIVE COLOR
  • { this.props.setColorAction("primary"); }} /> { this.props.setColorAction("info"); }} /> { this.props.setColorAction("success"); }} /> { this.props.setColorAction("warning"); }} /> { this.props.setColorAction("danger"); }} />
  • Download now
  • Documentation
  • Want more components?
  • Get pro version
); } } const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ setBgAction: (payload) => dispatch(setBgAction(payload)), setColorAction: (payload) => dispatch(setColorAction(payload)) }); export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);

And we are done, you can start the project and see how everything works fine:

Multiple reducers

As you can have multiple actions, you can have multiple reducers. The only thing is that you need to combine them — we’ll see this a bit further down.

Let’s go ahead and create two new reducers for our app, one for the setBgAction and one for the setColorAction:

1 — Linux / Mac commands

touch src/reducers/bgReducer.js touch src/reducers/colorReducer.js

2 — Windows commands

echo "" > src\reducers\bgReducer.js echo "" > src\reducers\colorReducer.js

After this, let’s create the reducers’ code as follows:

src/reducers/bgReducer.js

export default (state = {}, action) => { switch (action.type) { case "bgChange": return { ...state, bgColor: action.payload }; default: return state; } };

src/reducers/colorReducer.js

export default (state = {} , action) => { switch (action.type) { case "colorChange": return { ...state, activeColor: action.payload }; default: return state; } };

When working with combined reducers, you need to add a default state in each of your reducers that are going to be combined. In my case, I’ve chosen an empty object i.e. state = {};

And now, our rootReducer will combine these two as follows:

src/reducers/rootReducer.js

import { combineReducers } from 'redux'; import bgReducer from 'reducers/bgReducer'; import colorReducer from 'reducers/colorReducer'; export default combineReducers({ activeState: colorReducer, bgState: bgReducer });

So, we say that we want the colorReducer to be referred by the activeState prop of the app state, and the bgReducer to be referred by the bgState prop of the app state.

This means that our state will no longer look like this:

state = { activeColor: "color1", bgColor: "color2" }

It will now look like this:

state = { activeState: { activeColor: "color1" }, bgState: { bgColor: "color2" } }

Since we’ve changed our reducers, now we’ve now combined them together into just one, we need to change our store.js as well:

src/store.js

import { createStore } from "redux"; import rootReducer from "reducers/rootReducer"; // we need to pass the initial state with the new look function configureStore(state = { bgState: {bgColor: "black"}, activeState: {activeColor: "info"} }) { return createStore(rootReducer,state); } export default configureStore;

Since we’ve changed the way the state looks, we now need to change the props inside the Sidebar and FixedPlugin components to the new state object:

src/components/Sidebar/Sidebar.jsx:

Change line 36 from

to

src/components/FixedPlugin/FixedPlugin.jsx:

We need to change all the this.props.bgColor to this.props.bgState.bgColor . And all the this.props.activeColor to this.props.activeState.activeColor .

So the new code should look like this:

import React, { Component } from "react"; import Button from "components/CustomButton/CustomButton.jsx"; import { connect } from "react-redux"; import setBgAction from "actions/setBgAction"; import setColorAction from "actions/setColorAction"; class FixedPlugin extends Component { constructor(props) { super(props); this.state = { classes: "dropdown show" }; this.handleClick = this.handleClick.bind(this); } handleClick() { if (this.state.classes === "dropdown") { this.setState({ classes: "dropdown show" }); } else { this.setState({ classes: "dropdown" }); } } render() { return ( 
    
  • SIDEBAR BACKGROUND
  • { this.props.setBgAction("black"); }} /> { this.props.setBgAction("white"); }} />
  • SIDEBAR ACTIVE COLOR
  • { this.props.setColorAction("primary"); }} /> { this.props.setColorAction("info"); }} /> { this.props.setColorAction("success"); }} /> { this.props.setColorAction("warning"); }} /> { this.props.setColorAction("danger"); }} />
  • Download now
  • Documentation
  • Want more components?
  • Get pro version
); } } const mapStateToProps = state => ({ ...state }); const mapDispatchToProps = dispatch => ({ setBgAction: (payload) => dispatch(setBgAction(payload)), setColorAction: (payload) => dispatch(setColorAction(payload)) }); export default connect(mapStateToProps, mapDispatchToProps)(FixedPlugin);

Let’s open the project again with npm start and see how everything works. Ta da!

Thanks for reading!

If you’ve enjoyed reading this tutorial please share it. I am very keen on hearing your thoughts about it. Just give this thread a comment and I’ll be more than happy to reply.

Special thanks should also go to Esther Falayi for his tutorial which has given me some much needed understanding on Redux.

Useful links:

  • Get the code for this tutorial from Github
  • Read more about ReactJS on their official website
  • Read more about Redux here
  • Read more about React-Redux
  • Check out our platform to see what we are doing and who we are
  • Get Paper Dashboard React from www.creative-tim.com or from Github
  • Read more about Reactstrap, the core of Paper Dashboard React

Find me on:

  • Email: [email protected]
  • Facebook: //www.facebook.com/NazareEmanuel
  • Instagram: //www.instagram.com/manu.nazare/
  • Linkedin: //www.linkedin.com/in/nazare-emanuel-ioan-4298b5149/