Make Tetris with react + Redux + immutable

Time:2022-1-23

Tetris is a classic game that has been enthusiastically implemented in various programming languages, and there are many versions of javscript. Doing Tetris well with react has become one of my goals.

stamphttps://chvin.github.io/react-tetrisPlay!

Open source address:https://github.com/chvin/react-tetris

Effect preview

Make Tetris with react + Redux + immutable

Recording at normal speed, smooth experience.

Responsive

Make Tetris with react + Redux + immutable

Not just screen adaptation, butResponsive operation using keyboard on PC and finger on mobile phone

Make Tetris with react + Redux + immutable

Data persistence

Make Tetris with react + Redux + immutable

What do you fear most when playing stand-alone games? power failure. By subscriptionstore.subscribe, store the state in localstorage and accurately record all States. When the web page is closed and refreshed, the program crashes, and the mobile phone is out of power, you can continue by reopening the connection.

Redux status Preview

Make Tetris with react + Redux + immutable

The Redux design manages all the states that should be stored, which is the guarantee of the above persistence.

The game framework uses react + Redux, in which immutable is added, and its instance is used as the state of redux. (for an introduction to react and Redux, see:Getting started with reactRedux Chinese document

1. What is immutable?

Immutable is data that cannot be changed once created. Any modification, addition or deletion of the immutable object will return a new immutable object.

First acquaintance

Let’s look at the following code:

function keyLog(touchFn) {
  let data = { key: 'value' };
  f(data);
  console. log(data.key); //  Guess what will print?
}

Don’t look at F, don’t know it’s rightdataWhat did you do? I can’t confirm what will be printed. But ifdataIt’s immutable. You can make sure it’s printedvalue

function keyLog(touchFn) {
  let data = Immutable.Map({ key: 'value' });
  f(data);
  console.log(data.get('key'));  // value
}

In JavaScriptObjectAndArrayReference assignment is used. The new object simply refers to the original object. Changing the new object will also affect the old object:

foo = {a: 1};  bar = foo;  bar.a = 2;
foo.a // 2

Although this can save memory, when the application is complex, the state is uncontrollable, which is a great hidden danger, and the advantages of saving memory are not worth the loss.

Immutable is different, corresponding to:

foo = Immutable.Map({ a: 1 });  bar = foo.set('a', 2);
foo.get('a') // 1

concise

stayReduxIn, its best practice is eachreducerBoth return a new object (array), so we often see such code:

// reducer
...
return [
   ...oldArr.slice(0, 3),
   newValue,
   ...oldArr.slice(4)
];

In order to return a new object (array), it has to look strange above, which will become more difficult when using deeper data structures.
Let’s take a look at immutable:

// reducer
...
return oldArr.set(4, newValue);

Isn’t it simple?

About “===

We know that forObjectAndArrayof===Comparison refers to the comparison of reference addresses rather than “value comparison”, such as:

{a:1, b:2, c:3} === {a:1, b:2, c:3}; // false
[1, 2, [3, 4]] === [1, 2, [3, 4]]; // false

For the above, only deepcopy can be usedDeepcompare ` to traverse the comparison, which is not only troublesome but also has good performance.

Let’s feel itImmutableGood practice!

map1 = Immutable.Map({a:1, b:2, c:3});
map2 = Immutable.Map({a:1, b:2, c:3});
Immutable.is(map1, map2); // true

// List1 = Immutable.List([1, 2, Immutable.List[3, 4]]);
List1 = Immutable.fromJS([1, 2, [3, 4]]);
List2 = Immutable.fromJS([1, 2, [3, 4]]);
Immutable.is(List1, List2); // true

There seems to be a breeze.

There is a problem in the performance optimization of reactBig move, is to useshouldComponentUpdate(), but it returns by defaulttrue, that is, it is always executedrender()Method, followed by virtual DOM comparison.

When using native attributes, in order to get the correct shouldcomponentupdatetrue or false, we have to use deepcopy and deepcompare to calculate the answer. The performance consumption is not cost-effective. With immutable, it becomes easy to compare the deep structure using the above method.

For Tetris, imagine a chessboardTwo dimensional array, the box that can be moved isShape (also a two-dimensional array)+coordinate。 The superposition of chessboard and square constitutes the final resultMatrix。 The above attributes in the game are controlled byImmutableConstruction, through its comparison method, can be easily writtenshouldComponentUpdate。 Source code:/src/components/matrix/index.js#L35

Immutable learning materials:

2. How to use immutable in Redux

Change the combinereducers originally provided by Redux to the above Library:

// rootReduers.js
// import { combineReducers } from 'redux'; //  Old method
import { combineReducers } from 'redux-immutable'; //  New method

import prop1 from './prop1';
import prop2 from './prop2';
import prop3 from './prop3';

const rootReducer = combineReducers({
  prop1, prop2, prop3,
});


// store.js
//The method of creating a store is the same as normal
import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);
export default store;

Through newcombineReducersThe store object will be converted into immutable, which will be slightly different when used in the container (but this is exactly what we want):

const mapStateToProps = (state) => ({
  prop1: state.get('prop1'),
  prop2: state.get('prop2'),
  prop3: state.get('prop3'),
  next: state.get('next'),
});
export default connect(mapStateToProps)(App);

3、Web Audio Api

There are many different sound effects in the game, but actually only one sound effect file is cited:/build/music.mp3。 With the help ofWeb Audio ApiThe ability to play sound effects with millisecond accuracy and high frequency is<audio>What the label can’t do. During the game, press and hold the direction key to move the box, and you can hear high-frequency sound effects.

Make Tetris with react + Redux + immutable

WAAIt is a new and relatively independent interface system, which has higher processing authority for audio files and more professional built-in audio effect. It is the recommended interface of W3C and can professionally deal with the requirements of “sound speed, volume, environment, sound color visualization, high frequency and sound direction”. The following figure introduces the use process of WAA.

Make Tetris with react + Redux + immutable

Where source represents an audio source, destination represents the final output, and destination is synthesized from multiple sources.
Source code:/src/unit/music.jsAjax loading MP3 is realized and converted to WAA to control the playback process.

WAASupport in the latest 2 versions of each browser(CanIUse

Make Tetris with react + Redux + immutable

You can see that the IE camp and most Android machines can’t be used, and the others are OK.

Web audio API learning materials:

4. Optimization of game experience

Technology:

  • Press the direction key, the trigger frequency of horizontal movement and vertical movement is different. The game can define the trigger frequency instead of the original event frequency. Source code:/src/unit/event.js

  • Moving left and right can delay the falling speed, but the delay is slightly smaller when moving against the wall; When the speed is level 6, the delay will ensure a complete horizontal movement in one line;

  • Register buttons at the same timetouchstartandmousedownEvents for responsive games. WhentouchstartWhen occurs, it is not triggeredmousedownAnd whenmousedownWhen an event occurs, the event element may not be triggered because the mouse moves awaymouseup, will listen at the same timemouseoutSimulate mouseup ‘. Source code:/src/components/keyboard/index.js

  • ListeningvisibilitychangeEvent, when the page is hidden and switched, the game will not proceed, and the switching back will continuefocusThe status is also written into redux. So when playing with a mobile phoneTelephoneWhen, the game progress will be saved; The PC won’t hear gameover when playing games. It’s a bit likeiosApplication switching.

  • stayarbitrarilyThe current state can also be restored by refreshing the web page at any time (such as when the box is eliminated or when the game is over);

  • The only pictures used in the game are CSS;
    Make Tetris with react + Redux + immutable

  • The game is compatible with chrome, Firefox, IE9 +, edge, etc;

Play method:

  • You can set the initial chessboard (ten levels) and speed (six levels) before the game starts;

  • 100 points for line 1, 300 points for line 2, 700 points for line 3 and 1500 points for line 4;

  • The falling speed of blocks will increase with the number of lines eliminated (one level will be added every 20 lines);

5. Experience in development

  • For allcomponentIt’s all writtenshouldComponentUpdate, the performance on the mobile phone has been significantly improved. When large and medium-sized applications encounter performance problems, writing shouldcomponentupdate will help you.

  • Stateless component`(Stateless Functional Components)There is no life cycle. Because of the previous factor, all components need life cycle shouldcomponentupdate, so stateless components are not used.

  • In webpack config. jsDevserver attribute in writeHost: ‘0.0.0.0’ `, which can be accessed by IP during development, not limited to localhost;

  • In ReduxstoreIt is not the only way to pass a method to a through connectcontainer, you can jump out of the component and take it out in other files for process control (dispatch). Source code:/src/control/states.js

  • It is very convenient to use react + Redux for persistence. Just store the Redux state and read it when each reduers is initialized.

  • Through configuration eslintrc. jsAnd webpack config. js, eslint ‘test is integrated in the project. Using eslint can make the code write according to the specification and effectively control the code quality. When developing (or building) non-conforming code, errors can be found through IDE and console. reference resources:Airbnb: react usage specification

6. Summary

  • As a practice application of react, in the process of implementation, it is found that there are still many details of a small “square” that can be optimized and polished. This is the time to test the carefulness and skill of a front-end engineer.

  • The optimization direction includes react itself, such as which states are saved by Redux and which states are given to the state of the component; Jumping out of the framework has many features of the product to play. In order to meet your needs, these will naturally promote the development of technology.

  • A project starts from scratch, functions accumulate slowly bit by bit, and it will be built into a tall building. Don’t be afraid of difficulties, just knock it up if you have an idea^_^