Review, Research, and Discussion
- What are the advantages of storing tokens in “Cookies” vs “Local Storage”
Local Storage
Pros: It’s convenient.
- It’s pure JavaScript and it’s convenient. If you don’t have a back-end and you’re relying on a third-party API, you can’t always ask them to set a specific cookie for your site.
- Works with APIs that require you to put your access token in the header like this: Authorization Bearer ${access_token}.
Cons: It’s vulnerable to XSS attacks.
An XSS attack happens when an attacker can run JavaScript on your website. This means that the attacker can just take the access token that you stored in your localStorage.
An XSS attack can happen from a third-party JavaScript code included in your website, like React, Vue, jQuery, Google Analytics, etc. It’s almost impossible not to include any third-party libraries in your site.
Cookies
Pros: The cookie is not accessible via JavaScript; hence, it is not as vulnerable to XSS attacks as localStorage.
-
If you’re using httpOnly and secure cookies, that means your cookies cannot be accessed using JavaScript. This means, even if an attacker can run JS on your site, they can’t read your access token from the cookie.
-
It’s automatically sent in every HTTP request to your server.
Cons: Depending on the use case, you might not be able to store your tokens in the cookies.
-
Cookies have a size limit of 4KB. Therefore, if you’re using a big JWT Token, storing in the cookie is not an option.
-
There are scenarios where you can’t share cookies with your API server or the API requires you to put the access token in the Authorization header. In this case, you won’t be able to use cookies to store your tokens.
- Explain 3rd party cookies.
Third-party cookies are cookies that are set by a website other than the one you are currently on. For example, you can have a “Like” button on your website which will store a cookie on a visitor’s computer, that cookie can later be accessed by Facebook to identify visitors and see which websites they visited.
- How do pixel tags work?
A tracking pixel (also called 1x1 pixel or pixel tag) is a graphic with dimensions of 1x1 pixels that is loaded when a user visits a webpage or opens an email. … The tracking pixel URL is the memory location on the server. When the user visits a website, the image with the tag is loaded from this server.
Document the following Vocabulary Terms
-
cookies: A cookie is a small amount of data generated by a website and saved by your web browser. Its purpose is to remember information about you, similar to a preference file created by a software application. … Cookies are also used to store user preferences for a specific site.
-
authorization: Authorization is a security mechanism to determine access levels or user/client privileges related to system resources including files, services, computer programs, data and application features. … Key factors contain user type, number and credentials, requiring verification and related actions and roles.
-
access control: Access control is a security technique that regulates who or what can view or use resources in a computing environment. … Physical access control limits access to campuses, buildings, rooms and physical IT assets. Logical access control limits connections to computer networks, system files and data.
-
conditional rendering: Conditional rendering is a term to describe the ability to render different user interface (UI) markup if a condition is true or false. In React, it allows us to render different elements or components based on a condition. This concept is applied often in the following scenarios: Rendering external data from an API.
worlds easiest guide to redux
What is Redux?
The official documentation for Redux reads:
Redux is a predictable state container for JavaScript apps.
Redux takes away some of the hassles faced with state management in large applications. It provides you with a great developer experience, and makes sure that the testability of your app isn’t sacrificed for any of those.
As you develop React applications, you may find that keeping all your state in a top-level component is no longer sufficient for you.
You may also have a lot of data changing in your application over time.
Redux helps solve these kinds of problems. Mind you, it isn’t the only solution out there.
Why use Redux?
One other reason a lot of developers love Redux is the developer experience that comes with it. A lot of other tools have begun to do similar things, but big credits to Redux.
Some of the nice things you get with using Redux include logging, hot reloading, time travel, universal apps, record and replay — all without doing so much on your end as the developer. These things will likely sound fancy until you use them and see for yourself.
Redux offers a tradeoff. It asks you to:
- Describe application state as plain objects and arrays.
- Describe changes in the system as plain objects.
- Describe the logic for handling changes as pure functions.
None of these limitations are required to build an app, with or without React. In fact these are pretty strong constraints, and you should think carefully before adopting them even in parts of your app.
Do you have good reasons for doing so?
These limitations are appealing to me because they help build apps that:
-
Persist state to a local storage and then boot up from it, out of the box.
-
Pre-fill state on the server, send it to the client in HTML, and boot up from it, out of the box.
-
Serialize user actions and attach them, together with a state snapshot, to automated bug reports, so that the product developers can replay them to reproduce the errors.
-
Pass action objects over the network to implement collaborative environments without dramatic changes to how the code is written.
-
Maintain an undo history or implement optimistic mutations without dramatic changes to how the code is written.
-
Travel between the state history in development, and re-evaluate the current state from the action history when the code changes, a la TDD.
-
Provide full inspection and control capabilities to the development tooling so that product developers can build custom tools for their apps.
-
Provide alternative UIs while reusing most of the business logic.
Getting Started with Redux
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library. It is tiny (2kB, including dependencies), but has a large ecosystem of addons available.
Redux Toolkit
Redux Toolkit is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.
RTK includes utilities that help simplify many common use cases, including store setup, creating reducers and writing immutable update logic, and even creating entire “slices” of state at once.
Whether you’re a brand new Redux user setting up your first project, or an experienced user who wants to simplify an existing application, Redux Toolkit can help you make your Redux code better.
Redux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:
# NPMnpm install @reduxjs/toolkit
# Yarnyarn add @reduxjs/toolkit
Create a React Redux App
The recommended way to start new apps with React and Redux is by using the official Redux+JS template or Redux+TS template for Create React App, which takes advantage of Redux Toolkit and React Redux’s integration with React components.
# Redux + Plain JS templatenpx create-react-app my-app --template redux
# Redux + TypeScript templatenpx create-react-app my-app --template redux-typescript
Redux Core
The Redux core library is available as a package on NPM for use with a module bundler or in a Node application:
# NPMnpm install redux
# Yarnyarn add redux
It is also available as a precompiled UMD package that defines a window.Redux global variable. The UMD package can be used as a <script>
tag directly.
For more details, see the Installation page.
Basic Example
The whole global state of your app is stored in an object tree inside a single store. The only way to change the state tree is to create an action, an object describing what happened, and dispatch it to the store. To specify how state gets updated in response to an action, you write pure reducer functions that calculate a new state based on the old state and the action.
import { createStore } from 'redux'
/**
* This is a reducer - a function that takes a current state value and an
* action object describing "what happened", and returns a new state value.
* A reducer's function signature is: (state, action) => newState
*
* The Redux state should contain only plain JS objects, arrays, and primitives.
* The root state value is usually an object. It's important that you should
* not mutate the state object, but return a new object if the state changes.
*
* You can use any conditional logic you want in a reducer. In this example,
* we use a switch statement, but it's not required.
*/
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)
// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.
store.subscribe(() => console.log(store.getState()))
// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}
Instead of mutating the state directly, you specify the mutations you want to happen with plain objects called actions. Then you write a special function called a reducer to decide how every action transforms the entire application’s state.
testing reducers
Reducers
Just a quick refresher on what reducer is before we go into testing and code. Redux documentation is still great, in fact it covers unit tests really well you don’t even have to read this post. To summarize it, the reducer is a pure function that takes the previous state and an action, and returns the next state.
Pure functions
Because it’s a pure function, it’s easy to test. It’s a function that produces no side effects; given the same input, will always return the same output; doesn’t rely on external state.
What to test
Usually reducer consists of initial state and switch statement to handle every action. I like to break down my store into different sub-stores and have separate reducers for each sub-store. Sometimes one switch/case may handle few actions, because the business logic is the same and outcome should be the same. Some example GET_POST and UPDATE_POST should update the same store and produce same outcome.
It’s important to test reducers. That’s where business logic should happen and where new application state is formed based on external (API) or internal response.
Boilerplate
As any unit test, it starts with boilerplate setup and writing empty tests just to outline what needs to be tested. I like to to test the initial state and then every switch/case in the reducer to see if action.payload will produce expected store. If you stop and think about it — it’s simple.
import reducer from './getPostsReducer';
import * as types from '../actions/posts/getPostsReduxAction';
import expect from 'expect';
describe('team reducer', () => {
it('should return the initial state');
it('should handle GET_POST_SUCCESS');
it('should handle UPDATE_POST_SUCCESS');
it('should handle GET_POST_FAIL');
it('should handle GET_POST_START');
});
Add tests and mock data
From here, we can just start filling in the tests one by one. I also like to create mock files and store the API response at the time of testing. So I can test agains “real” data. This strategy uncovered few accidental API changes where the response was changed without any warning. Instead of blaming anyone, we can just look at the mock data and see what’s expected on the front end.
import reducer from './getPostReducer';
import * as actions from '../actions/posts/getPost';
import { UPDATE_POST_SUCCESS } from '../actions/posts/updatePost';
import expect from 'expect';
import getPostMock from '../mocks/getPostMock';
describe('post reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {})).toEqual({});
});
it('should handle GET_POST_START', () => {
const startAction = {
type: actions.GET_POST_START
};
// it's empty on purpose because it's just starting to fetch posts
expect(reducer({}, startAction)).toEqual({});
});
it('should handle GET_POST_SUCCESS', () => {
const successAction = {
type: actions.GET_POST_SUCCESS,
post: getPostMock.data, // important to pass correct payload, that's what the tests are for ;)
};
expect(reducer({}, successAction)).toEqual(getPostMock.data);
});
it('should handle UPDATE_POST_SUCCESS', () => {
const updateAction = {
type: UPDATE_POST_SUCCESS,
post: getPostMock.data,
};
expect(reducer({}, updateAction)).toEqual(getPostMock.data);
});
it('should handle GET_POST_FAIL', () => {
const failAction = {
type: actions.GET_POST_FAIL,
error: { success: false },
};
expect(reducer({}, failAction)).toEqual({ error: { success: false } });
});
});
References
-
Dan Abramov Redux Tutorials Check it out
-
worlds easiest guide to redux Read full article
-
testing reducers Read full article
-
Redux Docs Check it out