\( \newcommand{\N}{\mathbb{N}} \newcommand{\R}{\mathbb{R}} \newcommand{\C}{\mathbb{C}} \newcommand{\Q}{\mathbb{Q}} \newcommand{\Z}{\mathbb{Z}} \newcommand{\P}{\mathcal P} \newcommand{\B}{\mathcal B} \newcommand{\F}{\mathbb{F}} \newcommand{\E}{\mathcal E} \newcommand{\brac}[1]{\left(#1\right)} \newcommand{\abs}[1]{\left|#1\right|} \newcommand{\matrixx}[1]{\begin{bmatrix}#1\end {bmatrix}} \newcommand{\vmatrixx}[1]{\begin{vmatrix} #1\end{vmatrix}} \newcommand{\lims}{\mathop{\overline{\lim}}} \newcommand{\limi}{\mathop{\underline{\lim}}} \newcommand{\limn}{\lim_{n\to\infty}} \newcommand{\limsn}{\lims_{n\to\infty}} \newcommand{\limin}{\limi_{n\to\infty}} \newcommand{\nul}{\mathop{\mathrm{Nul}}} \newcommand{\col}{\mathop{\mathrm{Col}}} \newcommand{\rank}{\mathop{\mathrm{Rank}}} \newcommand{\dis}{\displaystyle} \newcommand{\spann}{\mathop{\mathrm{span}}} \newcommand{\range}{\mathop{\mathrm{range}}} \newcommand{\inner}[1]{\langle #1 \rangle} \newcommand{\innerr}[1]{\left\langle #1 \right \rangle} \newcommand{\ol}[1]{\overline{#1}} \newcommand{\toto}{\rightrightarrows} \newcommand{\upto}{\nearrow} \newcommand{\downto}{\searrow} \newcommand{\qed}{\quad \blacksquare} \newcommand{\tr}{\mathop{\mathrm{tr}}} \newcommand{\bm}{\boldsymbol} \newcommand{\cupp}{\bigcup} \newcommand{\capp}{\bigcap} \newcommand{\sqcupp}{\bigsqcup} \newcommand{\re}{\mathop{\mathrm{Re}}} \newcommand{\im}{\mathop{\mathrm{Im}}} \newcommand{\comma}{\text{,}} \newcommand{\foot}{\text{。}} \)

Saturday, October 5, 2019

Record react template for own use

Hierarchy of files in this record:
in src/config/store we set
import { createStore, combineReducers } from "redux";
import playerReducer from "../features/Player/reducer";
import winPageReducer from "../features/WinPage/reducer";

const rootReducer = combineReducers({
    player: playerReducer,
    winPage: winPageReducer
});

const store = createStore(
    rootReducer /* preloadedState, */,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;
with history:
import { createHashHistory } from "history";
const history = createHashHistory({
  basename: "/",
  hashType: "slash"
});

export default history;
Having created a store, we connect it with our app:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import store from "./config/store";
import "bootstrap/dist/css/bootstrap.min.css";
//babel-loader @babel/core --dev needed


ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById("root")
);
Imagine we are writing game in react, a player has represented by a div element:
import React from "react";
import walkSprite from "./walkSprite.png";
import { connect } from "react-redux";
import handleMovement from "./movement";

function Player(props) {
    return (
        <div
            style={{
                position: "absolute",
                top: props.position[1],
                left: props.position[0],
                backgroundImage: `url("${walkSprite}")`,
                backgroundPosition: "0 0",
                width: "50px",
                height: "37px"
            }}
        ></div>
    );
}

function mapStateToProps(state) {
    return {
        ...state.player
    };
}

export default connect(mapStateToProps)(handleMovement(Player));
note that we hope the player to keep updated with the position state stored in redux store, we connect it and map the redux state to player's props. What is handleMovement function at the end of this code? Since each function should just do one job, we can now separate the jobs into different functions, and then equip Player component with a handleMovement functionality:
import store from "../../config/store";
import { spriteSize } from "../../config/spriteSize";
import { mapHeight, mapWidth } from "../../config/spriteSize.js";

export default function handleMovement(player) {
    function getNewPosition(direction) {
        const oldPosition = store.getState().player.position;
        switch (direction) {
            case "West":
                return [oldPosition[0] - spriteSize.x, oldPosition[1]];

            case "East":
                return [oldPosition[0] + spriteSize.x, oldPosition[1]];

            case "South":
                return [oldPosition[0], oldPosition[1] + spriteSize.y];

            case "North":
                return [oldPosition[0], oldPosition[1] - spriteSize.y];
        }
    }

    function dispatchMove(direction) {
        const newPosition = getNewPosition(direction);
        if (
            newPosition[0] < 0 ||
            newPosition[0] > mapWidth - spriteSize.x ||
            newPosition[1] < 0 ||
            newPosition[1] > mapHeight - spriteSize.y
        ) {
        } else {
            store.dispatch({
                type: "MOVE_PLAYER",
                payload: {
                    position: getNewPosition(direction)
                }
            });
        }
    }

    window.addEventListener("keydown", e => {
        e.preventDefault();

        if (e.keyCode === 37) {
            dispatchMove("West");
        }
        if (e.keyCode === 38) {
            dispatchMove("North");
        }
        if (e.keyCode === 39) {
            dispatchMove("East");
        }
        if (e.keyCode === 40) {
            dispatchMove("South");
        }
    });

    return player;
}
Note that we can get the state in redux store whenever we want by calling the store.getState() method. In every component we include the reducer and immediately include this reducer in store.js by plugging it into combine reducer. Our reducer is like:
const initialState = {
    position: [0, 0]
};

const playerReducer = (state = initialState, action) => {
    switch (action.type) {
        case "MOVE_PLAYER":
            return {
                ...state,
                ...action.payload
            };
        default:
            return state;
    }
};

export default playerReducer;

No comments:

Post a Comment