1

I have set of components where it would consist of input fields along with text rows. enter image description here As given in the image the users should be able to add categories and description. After adding them they will be rendered as a list of components. like this enter image description here Inside a category there will be tags as given in the above image and to add them i have to add a input component. This input component should be available only when the user clicks on the Add tag button below each category row. When a user clicks on it,it should enable the input(should render a input component inside the selected category row) and should be able to type the tag name on it and save it. I need to make this input field enable only when i click on the add tag button. and it should enable only in the selected category row. This is the code that i have tried.

import React, { Component, Fragment } from "react";
import { Button, Header, Input } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import ReactDOM from "react-dom";

class App extends Component {
  state = {
    category: "",
    description: "",
    categories: []
  };

  onChange = (e, { name, value }) => {
    this.setState({ [name]: value });
  };

  addCategory = () => {
    let { category, description } = this.state;
    this.setState(prevState => ({
      categories: [
        ...prevState.categories,
        {
          id: Math.random(),
          title: category,
          description: description,
          tags: []
        }
      ]
    }));
  };

  addTag = id => {
    let { tag, categories } = this.state;
    let category = categories.find(cat => cat.id === id);
    let index = categories.findIndex(cat => cat.id === id);
    category.tags = [...category.tags, { name: tag }];
    this.setState({
      categories: [
        ...categories.slice(0, index),
        category,
        ...categories.slice(++index)
      ]
    });
  };

  onKeyDown = e => {
    if (e.key === "Enter" && !e.shiftKey) {
      console.log(e.target.value);
    }
  };

  tags = tags => {
    if (tags && tags.length > 0) {
      return tags.map((tag, i) => {
        return <Header key={i}>{tag.name}</Header>;
      });
    }
  };

  enableTagIn = id => {};

  categories = () => {
    let { categories } = this.state;
    return categories.map(cat => {
      return (
        <Fragment key={cat.id}>
          <Header>
            <p>
              {cat.title}
              <br />
              {cat.description}
            </p>
          </Header>
          <Input
            name="tag"
            onKeyDown={e => {
              this.onKeyDown(e);
            }}
            onChange={this.onChange}
          />
          <Button
            onClick={e => {
              this.addTag(cat.id);
            }}
          >
            Add
          </Button>
          {this.tags(cat.tags)}
        </Fragment>
      );
    });
  };

  render() {
    return (
      <Fragment>
        {this.categories()}
        <div>
          <Input name="category" onChange={this.onChange} />
          <Input name="description" onChange={this.onChange} />
          <Button onClick={this.addCategory}>Save</Button>
        </div>
      </Fragment>
    );
  }
}

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

This is the codesandbox url. Any idea on how to achieve this?.

1 Answer 1

2

I changed your code by using function components and react hooks and i created category component which has it own state like this:

   import React, { Fragment } from "react";
import { Button, Header, Input } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import ReactDOM from "react-dom";

const App = () => {
  const [Category, setCategory] = React.useState({
    title: "",
    description: ""
  });
  const [Categories, setCategories] = React.useState([]);
  return (
    <div>
      {console.log(Categories)}
      <Input
        value={Category.title}
        onChange={e => setCategory({ ...Category, title: e.target.value })}
      />
      <Input
        value={Category.description}
        onChange={e =>
          setCategory({ ...Category, description: e.target.value })
        }
      />
      <Button onClick={() => setCategories([...Categories, Category])}>
        Save
      </Button>
      <div>
        {Categories.length > 0
          ? Categories.map(cat => <CategoryItem cat={cat} />)
          : null}
      </div>
    </div>
  );
};

const CategoryItem = ({ cat }) => {
  const [value, setvalue] = React.useState("");
  const [tag, addtag] = React.useState([]);
  const [clicked, setclicked] = React.useState(false);
  const add = () => {
    setclicked(false);
    addtag([...tag, value]);
  };
  return (
    <Fragment>
      <Header>
        <p>
          {cat.title}
          <br />
          {cat.description}
        </p>
      </Header>
      <Input
        name="tag"
        value={value}
        style={{ display: clicked ? "initial" : "none" }}
        onChange={e => setvalue(e.target.value)}
      />
      <Button onClick={() => (clicked ? add() : setclicked(true))}>Add</Button>
      <div>{tag.length > 0 ? tag.map(tagname => <p>{tagname}</p>) : null}</div>
    </Fragment>
  );
};

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

and here a sandbox

Sign up to request clarification or add additional context in comments.

2 Comments

Nice but im lookign for a React class implementation. and this input field should also be hidden when you add a tag
@TRomesh done check sandbox ! and there is no big difference between func and class component!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.