Redux with React JS

Understanding how redux works has been difficult for a beginner like me in general. so I’ve prepared a simple example to build on my other post that uses i18n package to internationalize your SPA using React JS.

This example maintains the state of my previously selected language in Redux store. You can find the completed code here in my Github profile.

Redux is quite an excellent State Managment Framework. I’m using the official react-redux package that takes care of a lot of stuff like one component that wraps your entire app also called higher-order component. This component will subscribe to your store. The rest of your components will be children to this wrapper component and will only get the parts of the state that they need.

You will then have at least one main component that listens to state changes passed down by the Provider from the store. You can specify which parts of the state it should listen to, and those pieces of the state will be passed down as props to that component (and then of course, it can pass those down to its own children). You can specify that by using the connect() function on your Parent level component and using the mapStateToPropsfunction as a first parameter.

The entire state of the Application is represented by a State tree which is read-only. And every time you need to modify the state, you need to dispatch an Action.

Action object structure should always have a type property. Any other custom property can be added.
Type property e.g. “INCREMENT”, “DECREMENT”

Pure functions are predictable. They always give the same output for the same set of arguments.
Impure functions have side-effects. They may make changes to the data with database or network calls.
Redux takes the previous state, dispatches an action and returns the next state as a new object. This function that describes the state mutation is called the reducer.
Reducers expect 2 arguments: state and action.

e.g.
const counter = (state=0, action) => {
	switch(action.type) {
		case 'INCREMENT':
			return state + 1;
		case 'DECREMENT':
			return state - 1;
		default:
			return state;
	}
}

The store binds together the 3 principles of Redux:

  • It holds the applications’s current state object.
  • It lets you dispatch actions.
  • When you create it, you need to specify the reducer that tells you how state is updated with actions.

Below is the code for the store initialization:

import { createStore, applyMiddleware } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
import { composeWithDevTools } from 'redux-devtools-extension';
import Immutable from 'immutable';

const persistConfig = {
  key: 'root',
  storage : storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer);

const initialState = {};

const middleware = [thunk];

const composeEnhancers = composeWithDevTools({
  serialize: {
    immutable: Immutable
  }
});

export const store = createStore(persistedReducer, initialState, composeEnhancers(applyMiddleware(...middleware)));
export const persistor = persistStore(store)

Below is the code for App Component wrapped under the Provider component:

import React, { Component, Suspense } from "react";
import { Route, Switch, BrowserRouter as Router } from "react-router-dom";
import { Provider } from 'react-redux';
import { store, persistor } from '../store';
import { PersistGate } from 'redux-persist/integration/react'
import HomePage from "./home/HomePage";
import AboutPage from "./about/AboutPage";
import Header from "./common/Header";
import PageNotFound from "./PageNotFound";
import CoursesPage from "./courses/CoursesPage";
import '../config/i18n'

export class App extends Component {
  
  render() {
    let data = localStorage.getItem('persist:root') && JSON.parse(JSON.parse(localStorage.getItem('persist:root')).langOptionsReducer).option.value;
    console.log(data)
    console.log(this.props)
    this.props.props.changeLanguage(data)

    return (
      < Provider
        store={store} >
        {/* https://github.com/rt2zz/redux-persist/blob/master/docs/PersistGate.md */}
        < PersistGate
          loading={<div>loading app...</div>}
          // onBeforeLift={onBeforeLift}
          persistor={persistor} >
      <div className="container-fluid">
        <Header />
        <Switch>
          <Route exact path="/" component={HomePage} />
          <Route path="/courses" component={CoursesPage} />
          <Route path="/about" component={AboutPage} />
          <Route component={PageNotFound} />
        </Switch>
      </div>
      </PersistGate >
      </Provider >
    );
  }
}

export default App;

Below is the code for the action method, changeLang that is dispatched when a language option is selected:

export const changeLang = (option)=> (dispatch) => {
    dispatch({type : 'langOptions',
    payload : option})
}

This is the code for the langOptions reducer that is fired by the action method:

const initialState={option:{}}

export default function(state=initialState, action){
    switch (action.type) {
        case 'langOptions':
            return {
                ...state,
                option:action.payload
            }
        default:
            return state
    }
}

All the reducers in your application need to be combined into a single reducer as in the index.js file under the reducers folder:

import { combineReducers } from 'redux';
import langOptionsReducer from './langOptions'

export default combineReducers({
    langOptionsReducer
})

React Redux provides a connect function for you to read values from the Redux store (and re-read the values when the store updates). The connect function takes two arguments, both optional: mapStateToProps : called every time the store state changes. It receives the entire store state, and should return an object of data this component needs. 2nd parameter is mapDispatchToProps which can either be a function or a full object of action creators.

Code for the HomePage with connect function using mapStateToProps argument as below:

import React, { Component } from "react";
import { Link } from "react-router-dom";
//import "../../node_modules/bootstrap/dist/css/bootstrap.css";
import Select from "react-select";
import { options } from "../../config/options";
import "../../config/i18n";
import { withTranslation } from "react-i18next";
import { connect } from "react-redux";
import { changeLang } from "../../actions/langOptions";

export class HomePage extends Component {
  constructor(props) {
    super(props);
    console.log("inside constructor");
    this.state = {
      lang: options[0]
    };
  }

  changeLang = lang => {
    const { i18n } = this.props;
    const { value } = lang;
    this.props.changeLang(lang);
    this.setState({ lang });
    i18n.changeLanguage(value);
  };

  componentWillReceiveProps(props) {
    console.log(props);
  }

  componentDidMount() {
    this.setState({
      lang: this.props.option.value ? this.props.option : options[0]
    });
    // this.props.i18n.changeLanguage(options[2].value);
  }

  render() {
    const { lang } = this.state;
    return (
      <React.Fragment>
        <div className="jumbotron">
          <h1>Courses Administration</h1>
          <Link to="about" className="btn btn-primary btn-lg">
            Learn more
          </Link>
        </div>
        <br />
        <div style={{ width: "200px" }}>
          <Select
            defaultValue={options[0]}
            options={options}
            value={lang}
            onChange={this.changeLang}
            className="App-Select"
          />
        </div>
      </React.Fragment>
    );
  }
}

const mapStateToProps = state => {
  return {
    option: state.langOptionsReducer.option
  };
};

export default connect(
  mapStateToProps,
  { changeLang }
)(withTranslation("translations")(HomePage));

Internationalization with React JS i18n package

Building Single Page Applications (SPA) in React JS that require multi-language support require a way for the user to be able to select the preferred language and all or specific text on the page or site should change.

React supports a package i18next that helps load the default language and save the preferred language which the user selects from the UI. react-i18next is built on top of i18next and is a powerful internationalization framework for React JS. This post explains i18n usage for v10 or above.

This example is built on top of my other post for Client-side routing here.

You can find the i18n demo code in my Github profile here.

You need to install the package react-i18next using npm as below:

npm install i18next
npm install react-i18next

Initialize the i18next store placed under the config folder as below:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "../locales/en";
import fr from "../locales/fr";
import it from "../locales/it";
import de from "../locales/de";
import es from "../locales/es";
i18n.use(initReactI18next).init({
  resources: {
    en,
    fr,
    it,
    de,
    es
  },
  fallbackLng: "en",
  debug: true,
  ns: ["translations"],
  defaultNS: "translations",
  keySeparator: false,
  interpolation: {
    escapeValue: false,
    formatSeparator: ","
  },
  react: {
    wait: true
  }
});
export default i18n;

The i18n package has a provider which would wrap your react App component.

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./components/App";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router } from "react-router-dom";
import { I18nextProvider } from "react-i18next";
import i18n from "./config/i18n";
import "bootstrap/dist/css/bootstrap.min.css";
ReactDOM.render(
  <I18nextProvider i18n={i18n}>
    <Router>
      <App />
    </Router>
  </I18nextProvider>,
  document.getElementById("root")
);

Maintain your translation files under the Locales folder and make sure the namespace matches the one provided in the i18next store initialization.
Example translation here:

{
  "translations": {
    "title": "<0>Acerca de</0>",
    "intro": "<0>Esta es la página Acerca de.</0>"
  }
}

The text in the About page is translated as below using the locales:

import React from "react";
import { Trans } from "react-i18next";

const AboutPage = () => (
  <div>
    <Trans i18nKey="title">
      <h2>About</h2>
    </Trans>
    <Trans i18nKey="intro">
      <p>This is the About page.</p>
    </Trans>
  </div>
);

export default AboutPage;

The i18nKey matches the specific text to be translated under the namespace.

The HomePage.js code has a drop-down change language event that will change the translation in the About page when selected.

changeLang = lang => {
    const { i18n } = this.props;
    const { value } = lang;
    this.props.changeLang(lang);
    this.setState({ lang });
    i18n.changeLanguage(value);
  };

The app is hosted on IIS Server. Check out the post here on how to host react app on IIS.

There is also a branch in Github here, that explains the i18n legacy version usage i.e. till v9.

SQL Server Instead Of trigger to prevent duplicates

You might face a scenario where you’re using a REST Web Service or WebAPI where multiple hits of the same request to the API from the User interface is causing duplicate inserts. As the hits come to the API at the same date time-stamp, the API check fails at the database level as that record won’t exist in the table at that point in time.

To prevent the duplicate from getting inserted, we have multiple options at the Database level and one of them is using Instead Of trigger.

Using the INSTEAD Of trigger, you can conditionally choose to INSERT into the table or take some other action as per the requirement.

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[PREVENT_DUP_INSERT] ON [dbo].[tbl_submission]
INSTEAD OF INSERT
AS
BEGIN
	SET NOCOUNT OFF;
	IF NOT EXISTS(
		SELECT 1 FROM dbo.tbl_submission
		WHERE question_uid=(SELECT inserted.q_uid FROM inserted)
		AND user_survey_instance_id=(SELECT user_inst_id FROM inserted)
	)
	BEGIN
		INSERT INTO dbo.tbl_submission(user_id,q_uid,[value],[group],user_inst_id,created_at,updated_at)
		SELECT user_id,q_uid,[value],[group],user_inst_id,created_at,updated_at FROM inserted
	END
	SELECT [id] FROM [dbo].[tbl_submission] WHERE @@ROWCOUNT > 0 AND [id] = scope_identity();
END

An id must be returned by the body of the INSTEAD OF trigger. This is required especially if you’re using an ORM like Entity Framework which may be give concurrency related exception.

You can also choose to apply UNIQUE constraint on select columns to prevent duplicates and handle the Insert exceptions in the API or Trigger itself. But in my case, since there are already few duplicate insertions and one of the columns was VARCHAR(MAX) which does not allow creating UNIQUE indexes.

Find word in SQL Server Varchar variable

I’ll be discussing two ways by which you can search for a string in a SQL Server variable:

Using CHARINDEX() Method:

This function is used to return the position of a substring in string. It’ll return 0 if the substring is not found.
This start position value of the word can be useful for other calculations as well.

DECLARE @strWord nvarchar(50)='My Test String'  

IF CHARINDEX('Test String',@strWord) > 0   
   PRINT 'Found'
ELSE  
    PRINT 'Not Found'
	
Output:
Found

Use the Index value in another variable for other calculations with CHARINDEX as below:

DECLARE @iDex INT
SELECT @iDex=CHARINDEX('Test String',@strWord)

Another way is to use the LIKE operator in SQL Server. This operator is also used in SQL queries’ WHERE clause e.g.

SELECT * FROM Tbl_Name WHERE mainStringCol LIKE '%Test String%'

The above query will fetch all rows where the mainStringCol contains the text “Test String”.

Now, to find a string in a Varchar variable using the LIKE operator, e.g. below:

DECLARE @txtMailBody nvarchar(MAX)='  Hello Mr Anderson...'
IF @txtMailBody LIKE '%Hello Mr%'
	PRINT 'Hello'
ELSE IF @txtMailBody LIKE '%Wad Up%'
	PRINT 'Wad Up'
ELSE
	PRINT 'Goof Up!'

Output:
Hello

What are Content-type and Accept Headers

I’ve often felt confused with the difference between the Content-type and Accept Headers that are passed along with a request to an API. I’ve used a fake Json REST API called JsonPlaceHolder to show the Json request and response with a Post request.

Accept Header tells the API that it is expecting the response in the specified media type e.g. application/json or application/xml.
Accept: application/json

And Content-Type tells the API about the media type of the request being sent in the request body e.g. application/json.
Content-Type: application/json

Both headers are sent along with the call to the API from the Client. Below I’m using the Postman Client to test the scenario for json request.
For the scenario, to test the response in XML format, the API should support the format provided in the Accept Header.

The 2nd image above shows the Json body being passed in the Post request for Creating the resource.

For a standard HTML Post request, the Content-Type Header will be one of the Standard types:

  1. application/x-www-form-urlencoded for simple ASCII text
  2. multipart/form-data for file upload support or non-ASCII text

Change ApplicationInsights Azure resource configuration in existing Web App

Application Insights is a Service on Microsoft Azure that lets you understand what users are actually doing on your App.
It also lets you diagnose any issues with it’s Powerful analytics tools and works with platforms including .Net, Java and Node.js.

The App Insights Instrumentation key is what is required to link your App with the resource on Azure.
If you already have an existing App Insights resource created through Visual Studio and you need to change it, then you can create another resource manually from the Azure Portal.

Once the App Insights resource is created, copy the Instrumentation key and replace it in your ApplicationInsights.config file. This lets you switch the ApplicationInsights resource for your Application.

Look for the InstrumentationKey tag in your ApplicationInsights.config file and replace. You might also need to change the InstrumentationKey in the HomePage JavaScript under Views folder added by App Insights SDK.

Start debugging your App and verify with your Live Metrics Stream in the App Insights resource that it is working.

Customize Logging fields in IIS for hosted website

Open IIS Manager on your Web Server and Select the Website for which you want to customize your logging fields. The changes can also be done at the Server level but that depends on the requirement.

Double-click on Logging icon.

Click on Select fields to select or remove any fields that you want in your IIS logs.

To add any custom field, click on the Add Field button as shown below and add the required header. The below example shows how you can get the Client IP information from the X-Forwarded-For Header (XFF) when the Website is hosted on a Server in a Load Balanced environment. The source of this information is in the Request header. The new log file will have an “_x” suffix to it’s name after modification.

The Logs directory shown above is where your Log files are saved. To identify the file name, check the Website ID under Sites on the left pane.
The Log file name format will be “W3SVC<ID>”.

Click on Apply on the Actions Pane on the right to apply the changes.