0
\$\begingroup\$

The code consists in making a list of elements sortable (with the drag on the right) and selectable (click on the text). However it seems a little too complex.

https://codesandbox.io/s/434p397p74

Would it be possible to simplify / improve?

What are the missing good practices?

PS: I am a beginner in React. I used a library for the sortable part and made the selectable part

index.js

import React from "react";
import ReactDOM from "react-dom";
import {
  SortableContainer,
  SortableElement,
  arrayMove
} from "react-sortable-hoc";
import { SelectableList, SelectableItem } from "./selectable";
import { SortableHandler } from "./sortable";

import "./styles.css";

function Item(props) {
  return (
    <li>
      <SelectableItem item={props.value} index={props.sortIndex} /> -{" "}
      <SortableHandler />
    </li>
  );
}
const SortableItem = SortableElement(Item);

function List({ children }) {
  return (
    <ul>
      <SelectableList>{children}</SelectableList>
    </ul>
  );
}
const SortableList = SortableContainer(List);

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"]
    };
  }

  onSortEnd = ({ oldIndex, newIndex }) => {
    this.setState({
      items: arrayMove(this.state.items, oldIndex, newIndex)
    });
  };

  render() {
    let items = [];
    for (let i in this.state.items) {
      items.push(
        <SortableItem
          value={this.state.items[i]}
          index={parseInt(i)}
          sortIndex={this.state.items[i]}
          key={i}
        />
      );
    }

    return (
      <SortableList
        onSortEnd={this.onSortEnd}
        useDragHandle={true}
        axis="y"
        lockAxis="y"
        lockOffset={["0%", "0%"]}
        lockToContainerEdges={true}
        useContainerAsSortableHelperParent={true}
      >
        {items}
      </SortableList>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

selectable.js

import React from "react";

const SelectableContext = React.createContext({
  activeIndex: null,
  setActiveIndex: () => {}
});

export class SelectableList extends React.Component {
  constructor(props) {
    super(props);
    this.container = React.createRef();

    this.state = {
      activeIndex: null,
      setActiveIndex: this.setActiveIndex.bind(this)
    };
    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentDidMount() {
    document.addEventListener("mousedown", this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClickOutside);
  }

  handleClickOutside(event) {
    if (
      this.container.current &&
      !this.container.current.contains(event.target)
    ) {
      this.setActiveIndex(null);
    }
  }

  setActiveIndex(index) {
    this.setState({
      activeIndex: index
    });
  }

  render() {
    return (
      <SelectableContext.Provider value={this.state}>
        <div className="selectable-container" ref={this.container}>
          {this.props.children}
        </div>
      </SelectableContext.Provider>
    );
  }
}

export class SelectableItem extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick(context) {
    context.setActiveIndex(this.props.index);
  }

  render() {
    return (
      <SelectableContext.Consumer>
        {context => {
          return (
            <span
              style={{
                color:
                  context.activeIndex === this.props.index ? "red" : "black"
              }}
              onClick={() => {
                this.onClick(context);
              }}
            >
              {this.props.item}
            </span>
          );
        }}
      </SelectableContext.Consumer>
    );
  }
}

sortable.js

import React from "react";
import { SortableHandle } from "react-sortable-hoc";

const SortableHandler = SortableHandle(function() {
  return (
    <span
      style={{ border: "1px solid black", padding: "2px", fontSize: "10px" }}
    >
      Drag
    </span>
  );
});

export { SortableHandler };

react-sortable-hoc

https://github.com/clauderic/react-sortable-hoc

\$\endgroup\$
  • \$\begingroup\$ Code added to the question \$\endgroup\$ – Alex83690 Oct 24 '18 at 12:09
  • \$\begingroup\$ I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have. \$\endgroup\$ – Toby Speight Oct 24 '18 at 12:42
  • \$\begingroup\$ That's fine, thanks. is the question still on hold? \$\endgroup\$ – Alex83690 Oct 24 '18 at 13:07
0
\$\begingroup\$

To allow the management of a state common to several components but whose hierarchy is indirect (which is the case here for the components managing the selection), the simplest is to use Redux.

Redux advantage

Official documentation :

https://redux.js.org/

A really good tutorial for beginners :

https://www.valentinog.com/blog/react-redux-tutorial-beginners/

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.