Redux, but not yet fully Redux

Redux

1. Create a Redux Store

Redux is a state management framework that can be used with many different web technologies, including React.

In Redux, a state object is responsible for the whole state of the application, which means that if there is a React project containing ten components and each component has its own local state. Then the whole state of the project will be defined as a single state object through Reduxstore.
This is an important principle of redux. Redux store is the only real source of application status.

This also means that if an application wants to update the status, it can only be executed through the Redux store. One way data flow can more easily monitor and manage the status in the application.

const reducer = (state = 5) => {
  return state;
}

const store = Redux.createStore(reducer);

Reduxstore is a state that saves and manages application state.
You can use createStore() in the Redux object to create a Reduxstore. This method takes the reducer function as a required function. No one cares what it is for the time being.

Here you declare a store variable and assign it to the createStore() method, and then pass in reducer as a parameter.

Here, the ES6 default parameter syntax is used to initialize the value of state to 5.

2. Get status from Redux Store

The Redux store object provides several methods to interact with it. You can use the getState() method to retrieve the current state saved in the Redux store object.

const store = Redux.createStore(
  (state = 5) => state
);

const currentState = store.getState();

Here, we simply rewrite the previous code and use store. In store Getstate() retrieves the state. And assigned to the variable currentState.

3. Define a Redux Action

Since Redux is a state management framework, updating the state is one of the core tasks.
In Redux, all status updates are triggered by dispatch action, which is just a JavaScript object that contains information about the action events that have occurred.

The Redux store receives these action objects and updates the corresponding status.
Sometimes, Redux action also carries some data, such as the user name after logging in. Although the data is optional, the action must have a type attribute, which represents the type of the action.

Redux action provides information about events that occur in the application to the Redux store, and then the store updates the status according to the actions that occur.

let action = {type: 'LOGIN'};

Writing a Redux action is as simple as declaring an object with a type attribute.
Declare an object action and set an attribute type for it, with the value set to the string 'LOGIN'.

4. Define an Action Creator

After creating an action, send the action to the Redux store so that it can update its status.

In Redux, you can define an action creator to complete this task. Action creator is just a JavaScript function that returns action, or action creator creates an object representing action events.

const action = {
  type: 'LOGIN'
}

function actionCreator() {
  return action;
}

Defines a function called actionCreator(), which returns an action object when called.

5. Distribute Action Event

The dispatch method is used to assign actions to the Redux store and call store Dispatch () sends the value returned from the action creator back to the store.

action creator returns an object with type attribute, which specifies the action that has occurred, and then this method dispatch es the action object to the Redux store.

store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });

These two are equivalent, and both dispatch action s of type LOGIN.

const store = Redux.createStore(
  (state = {login: false}) => state
);

const loginAction = () => {
  return {
    type: 'LOGIN'
  }
};

store.dispatch(loginAction());

The Redux store here has an initialization state object {login:'false'}, and an action creator named loginAction(), which returns an action of type LOGIN, and then dispatches the action of LOGIN to the Redux store by calling the dispatch method, and passes the action created by loginAction().

6. Process actions in the Store

After an action is created and dispatch ed, the Redux store needs to know if to respond to the operation.
This is the meaning of the reducer function.
It is responsible for responding to the action and then modifying the status. reducer takes state and action as parameters and always returns a new state.

This is the only function of reducer. There should be no other side effects of calling the API, for example, it should not have any other side effects. Reducer is just a pure function that accepts States and actions and then returns a new state.

Another key principle of Redux is that state is read-only.
The reducer function must always return a new state and never directly modify the state.
Redux does not force the change of state, but it must be enforced in the code of the reducer function.

const defaultState = {
  login: false
};

const reducer = (state = defaultState, action) => {
  // 
  if (action.type == 'LOGIN') {
    return {login: true};
  } else {
    return state;
  }
  // 
};

const store = Redux.createStore(reducer);

const loginAction = () => {
  return {
    type: 'LOGIN'
  }
};

If the reducer function here receives an action of type 'login', it will return a state object that sets login to true. Otherwise, the current state is returned.

The action of the current state and dispatch will be passed to the reducer, so you can use action Type gets the type of action directly.

7. Use the Switch statement to process multiple Actions

const defaultState = {
  authenticated: false
};

const authReducer = (state = defaultState, action) => {
  switch(action.type) {
    case "LOGIN":
      return {
        authenticated: true
      };
    case "LOGOUT":
      return {
        authenticated: false
      };
    default: 
      return defaultState;
  }
};

const store = Redux.createStore(authReducer);

const loginUser = () => {
  return {
    type: 'LOGIN'
  }
};

const logoutUser = () => {
  return {
    type: 'LOGOUT'
  }
};

Here are store, actions, and action creators.
In reducer, switch in JavaScript is used to respond to different action events.
This is the standard mode when writing the Redux reducer. The switch statement selects action Type and returns the corresponding authentication status.

Also write a default case in the switch statement to return the current state. Because once there are multiple reducers in the program, they will run when an action is dispatch ed, even if the action has nothing to do with the reducer.
So make sure to return the current state.

8. Use const to declare Action Types

A common practice when using Redux is to specify the operation type as read-only, and then reference these constants wherever they are used.
You can refactor the code you are using by using the const declaration of action types.

// 
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
// 

const defaultState = {
  authenticated: false
};

const authReducer = (state = defaultState, action) => {

  switch (action.type) {

    case LOGIN:
      return {
        authenticated: true
      }

    case LOGOUT:
      return {
        authenticated: false
      }

    default:
      return state;

  }

};

const store = Redux.createStore(authReducer);

const loginUser = () => {
  return {
    type: LOGIN
  }
};

const logoutUser = () => {
  return {
    type: LOGOUT
  }
};

Declare LOGIN and LOGOUT as values of type const.

const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';

Then authReducer() and action creator refer to these constants instead of string values.

Constants are usually written in full uppercase, which is Redux's standard practice.

9. Register Store listener

There is another way to access data on the Reduxstore object: store subscribe().
This allows listener functions to be subscribed to the store and called whenever an action is dispatch ed.
A simple use of this method is to subscribe to a function for the store, which only records a message every time an action is received and the store is updated.

const ADD = 'ADD';

const reducer = (state = 0, action) => {
  switch(action.type) {
    case ADD:
      return state + 1;
    default:
      return state;
  }
};

const store = Redux.createStore(reducer);

let count = 0;

const aasad = () => (count += 1);

store.subscribe(aasad);

store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);

A callback function aasad is written. Every time the store receives an action, it will increment the global variable count. And pass this function to store Subscribe() method.
(const aasad = () => (count += 1);)

10. Combine multiple reductions

When the state of an application begins to become more and more complex, it may be a better choice to divide the state into multiple parts.
On the contrary, the first principle of Redux: all application states are saved in a simple state object in the store.
Therefore, Redux provides reducer combination as the solution of complex state model.

Define multiple reducers to handle different parts of the application state, and then combine these reducers into a root reducer. Then pass the root reducer to the ReduxcreateStore() method.

In order to combine multiple reducers, Redux provides the combineReducers() method.
This method takes an object as a parameter and defines an attribute that associates the key with a specific reducer function. Redux will use the given key value as the name of the associated state.

In general, they are unique to some extent, and it is a good practice to create a reducer for the state of each application.
For example:

const rootReducer = Redux.combineReducers({
  auth: authenticationReducer,
  notes: notesReducer
});

The notes key is handled by notesReducer, which is how to combine multiple reducers to manage more complex application states. In this example, the state saved in the Redux store is an object containing auth and notes attributes.

11. Send Action Data to Store

In addition to containing type, action can also send specific data together with action, which is very common.
Because actions usually come from user interaction and often carry some data, Redux store often needs to know these data.

const ADD_NOTE = 'ADD_NOTE';

const notesReducer = (state = 'Initial State', action) => {
  switch(action.type) {
    case ADD_NOTE: 
      return action.text;
    default:
      return state;
  }
};

const addNoteText = (note) => {
  
  return {
    type: ADD_NOTE,
    text: note
  };
  
};

const store = Redux.createStore(notesReducer);

console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());

There are notesReducer() and addNoteText()action creator.
The addNoteText() function returns an action object that contains a type attribute with the value ADD_NOTE, and text attributes, which set the value to note through action creator.
(const addNoteText = (note) =>)
When you need to call action creator, you need to pass in specific note information that can access the object.

Then, the switch statement in notesReducer() has an option to handle the addNoteText() operation, as long as add exists_ A note type action should trigger a case. And it should return the text attribute on the incoming action as the new state.

12. Use middleware to handle asynchronous operations

Asynchronous operation is an inevitable part of web development.
In some cases, asynchronous requests need to be used in Redux applications. Redux middleware is specially designed for this purpose, which is called Redux Thunk middleware.

If you want to use Redux Thunk middleware, pass it as a parameter to redux applyMiddleware(). This function is then supplied to createStore() as the second optional parameter.

const store = Redux.createStore(
  asyncDataReducer,
  Redux.applyMiddleware(ReduxThunk.default)
);

Then, to create an asynchronous action, you need to return a function with dispatch as the parameter in the action creator. In this function, you can dispatch action and execute asynchronous requests.

const handleAsync = () => {
  return function(dispatch) {
    
    dispatch(requestingData());
    setTimeout(function() {
      let data = {
        users: ['Jeff', 'William', 'Alice']
      }
      
      dispatch(receivedData(data));
    }, 2500);
  }
};

Here, setTimeout() is used to simulate calling asynchronous requests. Usually, the dispatch action is performed before the asynchronous behavior is executed, so that the application state knows that some data is being requested (for example, this state displays the load icon).
Then, once the data is received, another action will be sent, and the completion time of this action will be used as a valid value of the data.

Here, we are passing dispatch as a parameter to this special action creator. When it is used for dispatch action, the middleware can handle the rest by directly passing the action to the dispatch.

const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'

const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }

const handleAsync = () => {
  return function(dispatch) {
    
    dispatch(requestingData());
    setTimeout(function() {
      let data = {
        users: ['Jeff', 'William', 'Alice']
      }
      
      dispatch(receivedData(data));
    }, 2500);
  }
};

const defaultState = {
  fetching: false,
  users: []
};

const asyncDataReducer = (state = defaultState, action) => {
  switch(action.type) {
    case REQUESTING_DATA:
      return {
        fetching: true,
        users: []
      }
    case RECEIVED_DATA:
      return {
        fetching: false,
        users: action.users
      }
    default:
      return state;
  }
};

const store = Redux.createStore(
  asyncDataReducer,
  Redux.applyMiddleware(ReduxThunk.default)
);

Two dispatch events are written in the action creator of handleAsync().
dispatchrequestingData() before setTimeout() (simulate API call).
Then, after receiving (simulating) data, dispatchreceivedData()action is used to pass in the data.

13. Write a counter with Redux

Now I have a general understanding of all the core principles of Redux (?).

How to create actions and action creator s, create a Redux store, dispatch actions through the store, and update the design status using pure reducer.
There is also how to use reducer composition to manage complex states and handle asynchronous operations.
These concepts are the core principles of Redux.

const INCREMENT = 'INCREMENT'; //Define a constant for the incremental action type
const DECREMENT = 'DECREMENT'; //Define a constant for the decrement action type

const counterReducer = (state = 0, action) => {
  switch(action.type) {
    case INCREMENT: {
      return state + 1;
    }
    case DECREMENT: {
      return state - 1;
    }
    default: 
      return state;
  }
}// Defines a counter that increases or decreases the status based on the received action

const incAction = () => {
  return {type: 'INCREMENT'};
} // Define an action creator for increment

const decAction = () => {
  return {type: 'DECREMENT'};
} // Define an action creator for decrement

const store = Redux.createStore(counterReducer); // Define a Redux store here to deliver your reducer

I've realized it, but I haven't fully realized it yet.

14. Never change state

Immutable state means that the state will never be modified directly, but a new copy of the state will be returned.

Redux does not actively enforce state invariance in its store or reducer.
JavaScript provides some useful tools to enforce the invariance of state, whether it is string, number, array or object. (strings and numbers are original values and are essentially immutable)
States may include array or object because they are useful when representing data structures with many types of information.

const ADD_TO_DO = 'ADD_TO_DO';

const todos = [
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
];

const immutableReducer = (state = todos, action) => {
  switch(action.type) {
    case ADD_TO_DO:
      return state.concat(action.todo);
    default:
      return state;
  }
};

const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo
  }
}

const store = Redux.createStore(immutableReducer);

The concat method returns a new array without modifying the original array.
state.concat(action.todo);

15. Use extension operators in arrays

One solution for ES6 to help enforce state invariance in Redux is to extend the operator.

It has many functions, one of which is very suitable for generating a new array from an existing array. This is a relatively new but commonly used syntax.

let newArray = [...myArray];

newArray is now a clone of myArray, and the two arrays exist separately in memory.
If the execution is like newarray With a mutation like push (5), myArray will not change.
... Effectively propagate the values in myArray to the new array.

const immutableReducer = (state = ['Do not mutate state!'], action) => {
  switch(action.type) {
    case 'ADD_TO_DO':
      return [...state, action.todo];
    default:
      return state;
  }
};

const addToDo = (todo) => {
  return {
    type: 'ADD_TO_DO',
    todo
  }
}

const store = Redux.createStore(immutableReducer);

return [...state, action.todo];
I know everything.

16. Delete item from array

Deletes entries at a specific index.

const immutableReducer = (state = [0,1,2,3,4,5], action) => {
  switch(action.type) {
    case 'REMOVE_ITEM':
      return state.slice(0, action.index).concat(state.slice(action.index + 1, state.length));
    default:
      return state;
  }
};

const removeItem = (index) => {
  return {
    type: 'REMOVE_ITEM',
    index
  }
}

const store = Redux.createStore(immutableReducer);

Delete an item according to the index of the item, so there must be all the returned numbers except the item at the index.
Use slice(0, action.index) to get a slice from the index of the first item (including 0) to the index (excluding index).
Then use slice(action.index + 1, state.length) and add 1 from the index to get the second slice to the end.
Connect the two slices with concat and return. Only the items at the index are deleted in the returned new array.

17. Use object Assign copy object

When the state is an object, there are some methods to help enforce state invariance.
One method is object assign(). It takes the target object and the source object, and maps the attributes in the source object to the target object.
Any matching properties will be overwritten by the properties of the source object.
It is usually used to pass an empty object as the first parameter, and then make a shallow copy of the object with the object to be copied.

const newObject = Object.assign({}, obj1, obj2);

This will create a new object as a new object, which contains the properties currently existing in obj1 and obj2.

const defaultState = {
  user: 'CamperBot',
  status: 'offline',
  friends: '732,982',
  community: 'freeCodeCamp'
};

const immutableReducer = (state = defaultState, action) => {
  switch(action.type) {
    case 'ONLINE':
      return Object.assign({}, state, {status: "online"})
    default:
      return state;
  }
};

const wakeUp = () => {
  return {
    type: 'ONLINE'
  }
};

const store = Redux.createStore(immutableReducer);

Returns a new state object for type actionONLINE, which sets the status attribute to the string online.
(Object.assign({}, state, {status: "online"}))
Add all the contents of state to the empty object {}, and then overwrite {status: "online"}.

summary

Buddha said, you can only understand a little.

Added by timmah22 on Tue, 01 Feb 2022 22:10:22 +0200