Xi-Yuer

Xi-Yuer

github

React

React: A JavaScript Frontend Library Focused on UI#

Redux#

Redux is a state management library used for centralized management of shared state and data across the entire App component.

Store#

import { createStore } from 'redux'

const init = {
    value: 'defaultValue'
}

const store = createStore((state = init, action) => {
    const { type, data } = action
    switch (type) {
        case 'add':
            return { ...state, num: data }
        default:
            return state
    }
})
export default store

Dispatch Action#

import store from './store'

const actionCreator = (data) => {
    return {
        type: 'add',
        data
    }
}

btnClick = () => {
    const action = actionCreator(233)
    store.dispatch(action)
}

Get Store Value#

import store from './store'

class Home extends React.Component {
    componentDidMount() {
        this.unSubscribe = store.subscribe(() => {
            // Listen for store state changes to update the view
            this.setState({})
        })
    }
    componentWillUnmount() {
        // Unsubscribe
        this.unSubscribe()
    }
    render() {
        return (
            <>
                <h2> {store.getState().num}</h2>
            </>
        )
    }
}

React-Redux#

React-Redux makes it easier to dispatch actions and access data from the store within components, and data updates automatically re-render the components.

  • Provider is a component that wraps the outermost root component, allowing its child components to share state.
import React from 'react';
import ReactDOM from 'react-dom';

import { Provider } from 'react-redux';
import store from './store';

import App from './App';
ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
);
  • Sending actions and getting values from the store in functional components.
import React, { memo } from 'react';
import { useDispatch, useSelector } from 'react-redux';

export default memo(function Text() {
    const dispatch = useDispatch();
    const num = useSelector(state => state.num); // Get value

    const btnClick = () => {
        // Send action
        dispatch({
            type: 'add',
            num: 10,
        });
    };
    return (
        <div>
            <h2></h2>
            <button onClick={e => btnClick()}>Send Action</button>
        </div>
    );
});
  • Getting store and dispatching actions in class components ==> connect

    Connect can associate components with the store for data sharing.

import React from 'react'
import { connect } from 'react-redux'

class Home extends React.Component {
    const btnClick = () => {
        this.props.sendAction()
    }
    <>
        <button onClick={this.btnClick}>Dispatch</button>
    </>
}
export connect(
    // Parameter one: state in redux; Parameter two: component's own props
    // Return value: object, which will be passed to the component's props
    (state, ownProps) => { // Receive

    },
    // Parameter one: dispatch function for dispatching actions; Parameter two: own props
    // Return value: object, which will be passed to the Home component's props
    (dispatch, ownProps) => { // Send
        return {
            sendAction: () => {
                // Use dispatch to send an action
                dispatch({
                    type: 'add',
                    num: 1
                })
            }
        }
    }
)(Home)

Complete Code#

  • Store
import { createStore } from 'redux';

const init = {
    value: 'default value',
    num: 0,
};

function reducer(state = init, action) {
    const { type, data } = action;
    switch (type) {
        case 'add':
            return { ...state, num: data };
        case 'sub':
            return { ...state, num: data };
        default:
            return state;
    }
}

export default createStore(reducer);
  • Root Component
import React, { PureComponent } from 'react';
import { Provider } from 'react-redux';

import About from './pages/About';
import Home from './pages/Home';
import store from './store';

export default class App extends PureComponent {
    render() {
        return (
            <Provider store={store}>
                <Home ownProps={'Hehehe'} />
                <hr />
                <About />
            </Provider>
        );
    }
}
  • Class Component
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';

class Home extends PureComponent {
    btnClick = () => {
        this.props.addAction();
    };
    render() {
        return (
            <div>
                <h2>Home: Class Component</h2>
                <button onClick={this.btnClick}>+</button>
                <h2>Current Count: {this.props.num}</h2>
            </div>
        );
    }
}
const mapStateToProps = (state, ownProps) => {
    return state;
};
const mapDispatchToProps = (dispatch, ownProps) => {
    let num = 0;
    return {
        addAction: () => {
            dispatch({
                type: 'add',
                data: ++num,
            });
        },
    };
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
  • Functional Component
import React, { memo } from 'react';
import { useDispatch, useSelector } from 'react-redux';

export default memo(function About() {
    const dispatch = useDispatch();
    const num = useSelector(state => state.num);

    const btnClick = () => {
        let temNum = num;
        dispatch({
            type: 'sub',
            data: --temNum,
        });
    };
    return (
        <div>
            <h2>About: Functional Component</h2>
            <button onClick={btnClick}>-</button>
            <h2>Current Count: {num}</h2>
        </div>
    );
});

combineReducers(reducers)#

  • reducers/todos.js
export default function todos(state = [], action) {
    switch (action.type) {
        case 'ADD_TODO':
            return state.concat([action.text]);
        default:
            return state;
    }
}
  • reducers/counter.js
export default function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':
            return state - 1;
        default:
            return state;
    }
}
  • reducers/index.js
import { combineReducers } from 'redux';
import todos from './todos';
import counter from './counter';

export default combineReducers({
    todos,
    counter,
});
  • App.js
import { createStore } from 'redux';
import reducer from './reducers/index';

let store = createStore(reducer);
console.log(store.getState());
// {
//   counter: 0,
//   todos: []
// }

store.dispatch({
    type: 'ADD_TODO',
    text: 'Use Redux',
});
console.log(store.getState());
// {
//   counter: 0,
//   todos: [ 'Use Redux' ]
// }

Mobx#

Store#

import { action, computed, runInAction, makeAutoObservable, flow } from 'mobx';

export class CounterStore {
    // Define data
    count = 0;
    list = [1, 2, 3, 4, 5];
    asyncData = 0;
    flowData = 'default';
    constructor() {
        // Make data reactive
        makeAutoObservable(this, {
            // Can be marked or not
            incrementCount: action,
            decrementCount: action,
            changeFilterList: action,
            asyncAdd: action,
            asyncFlow: action.bound,
            filterList: computed,
        });
    }
    // Define action functions to modify data
    incrementCount = () => {
        this.count++;
    };
    decrementCount = () => {
        this.count--;
    };
    changeFilterList = () => {
        this.list.push(...[5, 4, 3, 2, 1]);
    };

    // Asynchronous action (wrapped in runInAction)
    asyncAdd = async () => {
        await Promise.resolve();
        runInAction(() => {
            this.incrementCount();
            this.asyncData++;
        });
    };
    // Asynchronous action (using flow)
    asyncFlow = flow(function* () {
        yield console.log(this); // Needs to bind this
        const data = yield Promise.resolve('Flow');
        this.flowData = data;
    });

    // Define computed
    get filterList() {
        return this.list.map(item => item * 2);
    }
}

Root Store#

import { CounterStore } from './counter';

class Store {
    constructor() {
        this.counter = new CounterStore();
    }
}
const store = new Store();
export { store };

Store Sharing#

Using context

import React, { createContext } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { store } from './store';
export const Context = createContext();

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Context.Provider value={store}>
        <App />
    </Context.Provider>
);

Using in Components#

import React, { memo, useContext } from 'react';

import { observer } from 'mobx-react-lite';
import { Context } from '../..';

const Counter = () => {
    const {
        counter: {
            count,
            changeFilterList,
            asyncData,
            flowData,
            decrementCount,
            filterList,
            incrementCount,
            asyncAdd,
            asyncFlow,
        },
    } = useContext(Context);
    return (
        <div>
            <button onClick={incrementCount}>+</button>
            <span>{count}</span>
            <button onClick={decrementCount}>-</button>
            {filterList.map((i, n) => {
                return <li key={n}>{i}</li>;
            })}
            <button onClick={changeFilterList}>Add Array</button>
            <hr />
            <span>{asyncData}</span>
            <button onClick={asyncAdd}>Asynchronous runInAction</button>
            <hr />
            <span>{flowData}</span>
            <button onClick={asyncFlow}>Asynchronous flow</button>
        </div>
    );
};

export default memo(observer(Counter));
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.