Совет по решению задачи на реакте

85
20 марта 2022, 17:40

У меня есть массив объектов, который я вывожу map'ом в jsx. Так же при нажатии на кнопку enter на элементе массива, я вызываю функцию с аргументом id объекта. Мне нужно что бы при нажатии на arrowKeys я переключал активный класс на каждом элементе массива, и при нажатии на enter, что бы вызывалась функция на том элементе на котором сейчас стоит активный класс или например дата атрибут. То есть по сути, мне нужно как то выбирать каждый item массива стрелками. Как лучше это решить?

Answer 1

там песочница

cложность в фокусировке

html

  • tabindex - https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex

js

  • focus - https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus

но если мы используем document.body.addEventListener какой тогда смысл в onKeyDown на каком-то одном компонентике, если можно "слушать" body ...

js вариант фокусировки закомментирован, реализован в блоке - ,- focus

тут запустить можно

необходимо кликнуть по контейнеру

const Cell = ({ children, ...props }) => (React.createElement("span", Object.assign({}, props), children)); 
const Row = ({ children, ...props }) => (React.createElement("div", Object.assign({}, props), children)); 
// -,- 
var KEYS; 
(function (KEYS) { 
    KEYS["ArrowUp"] = "ArrowUp"; 
    KEYS["ArrowDown"] = "ArrowDown"; 
    KEYS["ArrowLeft"] = "ArrowLeft"; 
    KEYS["ArrowRight"] = "ArrowRight"; 
    KEYS["Enter"] = "Enter"; 
})(KEYS || (KEYS = {})); 
// -,- 
const Matrix = ({ matrix }) => { 
    const cellStyle = { 
        display: 'inline-block', textAlign: 'center' 
    }; 
    cellStyle.lineHeight = cellStyle.width = cellStyle.height = '30px'; 
    // -,- 
    const [key, setKey] = React.useState('key'); 
    const [xy, setXY] = React.useState({ x: 0, y: 0 }); 
    let { x, y } = xy; 
    const reset = () => { ({ x, y } = xy); }; 
    const xMax = matrix[0].length - 1; 
    const yMax = matrix.length - 1; 
    // -,- 
    const handleKeyPress = (event) => { 
        const { key } = event; 
        // -,- 
        setKey(key); 
        // -,- 
        const d = { 
            [KEYS.ArrowUp]: () => --y, 
            [KEYS.ArrowDown]: () => ++y, 
            [KEYS.ArrowLeft]: () => --x, 
            [KEYS.ArrowRight]: () => ++x, 
            [KEYS.Enter]: () => { }, 
        }; 
        // -,- 
        const isTrue = q => true === q; 
        const isOne = (x, y) => 1 === matrix[y][x]; 
        const ch = { 
            [KEYS.ArrowUp]: () => [y > 0,].every(isTrue), 
            [KEYS.ArrowDown]: () => [y < yMax,].every(isTrue), 
            [KEYS.ArrowLeft]: () => [x > 0,].every(isTrue), 
            [KEYS.ArrowRight]: () => [x < xMax,].every(isTrue), 
            [KEYS.Enter]: () => [true].every(isTrue), 
        }; 
        // -,- 
        const isOK = key in ch && key in d; 
        if (!isOK) { 
            console.warn('key in ch', key in ch); 
            console.warn('key in d', key in d); 
            return; 
        } 
        while (ch[key]()) { 
            d[key](); 
            if (isOne(x, y)) { 
                setXY({ x, y }); 
                break; 
            } 
        } 
        if (!isOne(x, y)) 
            reset(); 
        // -,- 
    }; 
    // -,- 
    // - ,- focus 
    let div = null; 
    // div = React.useRef<HTMLDivElement>() 
    // let [f, setF] = React.useState(false) 
    // React.useEffect(() => { 
    //     const list = ['click', 'load']; 
    //     const focus = () => { 
    //         if (div.current) { 
    //             div.current.focus() 
    //         } 
    //     } 
    //     list.forEach(str => document.body.addEventListener(str, focus)) 
    //     if (!f) { 
    //         console.log({ xMax, yMax }) 
    //         setF(true) 
    //     } 
    //     return () => { 
    //         list.forEach(str => document.body.removeEventListener(str, focus)) 
    //     } 
    // }) 
    // -, - focus 
    const qq = { 
        backgroundColor: 'antiquewhite', 
        marginLeft: 'auto', 
        marginRight: 'auto', 
        width: '150px', 
    }; 
    // -,- 
    return (React.createElement("div", { tabIndex: 0, onKeyDown: handleKeyPress, style: qq, ref: div }, 
        React.createElement("p", null, key), 
        React.createElement("p", null, 
            x, 
            " ", 
            y), 
        matrix.map((arr, i) => React.createElement(Row, null, arr.map((q, j) => React.createElement(Cell, { style: (j === x && i === y) ? { ...cellStyle, backgroundColor: 'gray' } : cellStyle }, q)))))); 
}; 
// -,- 
const matrix = [ 
    [1, 1, 1, 1, 1], 
    [1, 1, 1, 1, 1], 
    [1, 1, 0, 1, 1], 
    [1, 1, 1, 0, 1], 
    [1, 1, 1, 1, 0], 
]; 
// -,- 
ReactDOM.render(React.createElement(Matrix, { matrix: matrix }), document.body);
<script src="https://unpkg.com/react@^16/umd/react.production.min.js"></script> 
<script src="https://unpkg.com/react-dom@^16.7.0/umd/react-dom.production.min.js"></script> 
<script> 
console.log("useState" in React) 
console.log("render" in ReactDOM) 
</script>

upd: и всё же я бы так написал

class KeyboardKeyProvider extends React.Component<KeyboardKeyProvider.$props, KeyboardKeyProvider.$state> {
    state = { key: '' } as KeyboardKeyProvider.$state;
    hK = (e: KeyboardEvent) => {
        const { key } = e;
        // console.log({ key })
        if (key in KEYS)
            this.setState({ key });
    };
    componentDidMount() {
        window.addEventListener('keydown', this.hK);
    }
    componentWillUnmount() {
        window.removeEventListener('keydown', this.hK);
    }
    render() {
        const Co = this.props.children;
        const { key: keyboardKey } = this.state;
        return (<div>
            {Co({ keyboardKey })}
        </div>);
    }
}
namespace KeyboardKeyProvider {
    interface childrenProps {
        keyboardKey: KEYS;
    }
    export interface $props extends React.ComponentProps<'div'> {
        children: React.FC<React.ComponentProps<'div'> & childrenProps>;
    }
    export interface $state {
        key: any;
    }
}

const Cell = ({ children, ...props }) => (React.createElement("span", Object.assign({}, props), children)); 
var KEYS; 
(function (KEYS) { 
    KEYS["ArrowUp"] = "ArrowUp"; 
    KEYS["ArrowDown"] = "ArrowDown"; 
    KEYS["ArrowLeft"] = "ArrowLeft"; 
    KEYS["ArrowRight"] = "ArrowRight"; 
    KEYS["Enter"] = "Enter"; 
})(KEYS || (KEYS = {})); 
class KeyboardKeyProvider extends React.Component { 
    constructor() { 
        super(...arguments); 
        this.state = { key: '' }; 
        this.hK = (e) => { 
            const { key } = e; 
            // console.log({ key }) 
            if (key in KEYS) 
                this.setState({ key }); 
        }; 
    } 
    componentDidMount() { 
        window.addEventListener('keydown', this.hK); 
    } 
    componentWillUnmount() { 
        window.removeEventListener('keydown', this.hK); 
    } 
    render() { 
        const Co = this.props.children; 
        const { key: keyboardKey } = this.state; 
        return (React.createElement("div", null, Co({ keyboardKey }))); 
    } 
} 
const cellStyle = { 
    display: 'inline-block', textAlign: 'center' 
}; 
cellStyle.lineHeight = cellStyle.width = cellStyle.height = '30px'; 
const conteinerStyle = { 
    backgroundColor: 'antiquewhite', 
    marginLeft: 'auto', 
    marginRight: 'auto', 
    width: '150px', 
}; 
class Matrix extends React.Component { 
    constructor() { 
        super(...arguments); 
        this.state = new Matrix.State(this.props.matrix, this.props.keyboardKey); 
    } 
    //-------------------------------------------------------------------- 
    // OLD style // По этому названию он будет доступен до 17 версии 
    // https://ru.reactjs.org/docs/react-component.html#unsafe_componentwillupdate 
    // componentWillUpdate(nP: Readonly<Matrix.$props>, S) { 
    //     const { keyboardKey } = nP 
    //     this.qqp(keyboardKey) 
    // } 
    render() { 
        const { keyboardKey } = this.state; 
        const { x, y } = this.state.xy; 
        return (React.createElement("div", { style: conteinerStyle }, 
            React.createElement("p", null, keyboardKey || '_'), 
            React.createElement("p", null, 
                x, 
                " ", 
                y), 
            matrix.map((arr, i) => React.createElement(Row, { key: `row-${i}` }, arr.map((q, j) => React.createElement(Cell, { key: `cell-${j}`, style: (j === x && i === y) ? { ...cellStyle, backgroundColor: 'gray' } : cellStyle }, q)))))); 
    } 
} 
Matrix.qqp = (nextProps, prevState) => { 
    const { keyboardKey } = nextProps; 
    if (!keyboardKey) 
        return; 
    // -,- 
    let { xy, max } = prevState; 
    let { x, y } = xy; 
    const resetXY = () => { ({ x, y } = xy); }; 
    const setXY = ({ x, y }) => { xy = { x, y }; }; 
    // -,- 
    const d = { 
        [KEYS.ArrowUp]: () => --y, 
        [KEYS.ArrowDown]: () => ++y, 
        [KEYS.ArrowLeft]: () => --x, 
        [KEYS.ArrowRight]: () => ++x, 
        [KEYS.Enter]: () => { }, 
    }; 
    // -,- 
    const isTrue = q => true === q; 
    const isOne = (x, y) => 1 === matrix[y][x]; 
    const ch = { 
        [KEYS.ArrowUp]: () => [y > 0,].every(isTrue), 
        [KEYS.ArrowDown]: () => [y < max.y,].every(isTrue), 
        [KEYS.ArrowLeft]: () => [x > 0,].every(isTrue), 
        [KEYS.ArrowRight]: () => [x < max.x,].every(isTrue), 
        [KEYS.Enter]: () => [true].every(isTrue), 
    }; 
    // -,- 
    const isOK = keyboardKey in ch && keyboardKey in d; 
    if (!isOK) { 
        console.warn('key in ch', keyboardKey in ch); 
        console.warn('key in d', keyboardKey in d); 
        return; 
    } 
    while (ch[keyboardKey]()) { 
        d[keyboardKey](); 
        if (isOne(x, y)) { 
            setXY({ x, y }); 
            break; 
        } 
    } 
    if (!isOne(x, y)) { 
        resetXY(); 
    } 
    // -,- 
    return { xy, keyboardKey }; 
}; 
Matrix.getDerivedStateFromProps = (nextProps, prevState) => { 
    // Re-run the filter whenever the list array or filter text change. 
    // Note we need to store prevPropsList and prevFilterText to detect changes. 
    return Matrix.qqp(nextProps, prevState); 
    // --,-- 
    // return null; 
}; 
(function (Matrix) { 
    class State { 
        constructor(matrix, keyboardKey) { 
            this.matrix = matrix; 
            this.keyboardKey = keyboardKey; 
            this.xy = { x: 0, y: 0 }; 
            this.max = { 
                x: this.matrix[0].length - 1, 
                y: this.matrix.length - 1, 
            }; 
        } 
    } 
    Matrix.State = State; 
})(Matrix || (Matrix = {})); 
const Row = ({ children, ...props }) => (React.createElement("div", Object.assign({}, props), children)); 
const matrix = [ 
    [1, 1, 1, 1, 1], 
    [1, 1, 1, 1, 1], 
    [1, 1, 0, 1, 1], 
    [1, 1, 1, 0, 1], 
    [1, 1, 1, 1, 0], 
]; 
// -,- 
ReactDOM.render(React.createElement(React.Fragment, null, 
    React.createElement(KeyboardKeyProvider, null, ({ keyboardKey }) => React.createElement(Matrix, Object.assign({}, { matrix, keyboardKey })))), document.body);
    <script src="https://unpkg.com/react@^16/umd/react.production.min.js"></script> 
    <script src="https://unpkg.com/react-dom@^16.7.0/umd/react-dom.production.min.js"></script> 
    <script> 
    console.log("useState" in React) 
    console.log("render" in ReactDOM) 
    </script>

READ ALSO
Google charts с видоискателем

Google charts с видоискателем

Подскажите, как построить с помощью Google charts график из видоискателем по времени типа http://nvd3org/examples/lineWithFocus

66
Proxy для вложенных объектов

Proxy для вложенных объектов

Всем привет! Я относительно недавно пришёл в JS и время от времени люблю создавать свои велосипеды чтобы понять лучше сам язык и реализацию...

222
Якори и fullPage.js

Якори и fullPage.js

В общем задача такая, у меня есть макет, на котором нужно реализовать плагин fullPage, также там на главной странице имеются несколько якорей,...

69