10 Replies - 449 Views - Last Post: 25 August 2017 - 11:59 AM

#1 general656  Icon User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 287
  • Joined: 25-March 15

ReactJS+Mobx - How to create a Dynamic List?

Posted 15 August 2017 - 02:05 PM

If you can answer this question
Login in Stackoverflow to get points:
StackOverflow Question

I have a small problem. I wish someone can answer :/

I want to create a Dynamic List that you can modify it by removing whichever item you want from the list and re-render the list in respect of the modified objects.

An example:


But my example for some reason always removes the last item, no matter which I select.

My code is this:
import React, {Component} from 'react';
import {observer} from 'mobx-react';
import {observable, action} from 'mobx';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AutoComplete from 'material-ui/AutoComplete';
import TextField from 'material-ui/TextField';
import TextArea from '../TextArea/index';
import {
Panel,
Col,
Form
} from 'react-bootstrap';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import ContentAdd from 'material-ui/svg-icons/content/add';
import ContentRemove from 'material-ui/svg-icons/content/remove';
import './styles.scss';

@observer export default class ListInterface extends Component {
    @observable dataSource = [];
    @observable itemToAdd = "";
    constructor(props) {
        super(props);
        let {id} = this.props;
        this.state = { id, dataSource: this.dataSource };
        this.createUI = this.createUI.bind(this);
    }
    @action updateItemToAdd(searchText, dataSource, params) {
        this.itemToAdd = searchText;
    }
    @action handleChange(searchText, dataSource, params, index) {
        this.dataSource[index] = dataSource;
    }

    @action addObject() {
        this.dataSource.push({productName: this.itemToAdd});
        console.log("Added Component!");
        console.log(this.itemToAdd);
        console.log("----------------------------\n");
        this.itemToAdd = "";
    }

    @action removeObject(index) {
        console.log("Removed Component!");
        console.log(this.dataSource[index].productName);
        this.dataSource.splice(index-1, 1);
        console.log(this.dataSource);
        console.log("----------------------------\n");
    }

    getObject(i) {
        return this.dataSource[i];
    }

    getList() {
        return this.dataSource.slice();
    }

    createUI() {
        let uiItems = [];
        let i;
        for(i = 0; i < this.dataSource.length; i++) {
            uiItems.push(
                <div className="listItem" key={i}>
                    <Col sm={10}>
                        <MuiThemeProvider>
                            <TextField
                                id={i.toString()}
                                value={this.state.dataSource[i].productName}
                                floatingLabelText="Product"
                                disabled={true}
                                fullWidth={true}
                            />
                        </MuiThemeProvider>
                    </Col>
                    <Col sm={2}>
                        <MuiThemeProvider id={i.toString()}>
                            <FloatingActionButton
                                mini={true}
                                secondary={true}
                                onclick={this.removeObject.bind(this, i)}
                                className="listButton"
                            >
                                <ContentRemove/>
                            </FloatingActionButton>
                        </MuiThemeProvider>
                    </Col>
                </div>
            );
        }

        uiItems.push(
            <div className="listAddItem" key={this.dataSource.length}>
                <Col sm={10}>
                    <MuiThemeProvider>
                        <AutoComplete
                            id={i.toString()}
                            floatingLabelText="Product"
                            dataSource={["Apostolis", "Orestis", "Germanos", "Kwnstantina", "Tasos"]}
                            openonfocus={true}
                            onUpdateInput={this.updateItemToAdd.bind(this)}
                            fullWidth={true}

                        />
                    </MuiThemeProvider>
                </Col>
                <Col sm={2}>
                    <MuiThemeProvider>
                        <FloatingActionButton
                            mini={true}
                            className="listButton"
                            onclick={this.addObject.bind(this)}
                        >
                            <ContentAdd/>
                        </FloatingActionButton>
                    </MuiThemeProvider>
                </Col>
            </div>
        );

        return uiItems;
    }

    render() {
        let {id} = this.state;
        return (
            <div id={id} className="listInterface">
                <Panel header={id}>
                    <Form>
                        {this.createUI()}
                    </Form>
                </Panel>
            </div>
        );
    }
}


Flow: (my understanding so far)

render() -> Calls createUI()

createUI() -> Returns a list of Controlled Components by @observable variables

Plus Sign Button -> Calls addObject(), which adds an @observable itemToAdd in the dataSource list -> This causes re-rendering which means React calls createUI() again? Then it renders again all the item in the dataSource list?

Minus Sign Button
-> Calls removeObject(), which removes the object in the index given from the binded Component -> Then it should "re-render" createUI() function in respect to the new data in dataSource. But a bug happens and instead the last visualized component is removed, but the correct Object from dataSource is removed. Why?

This post has been edited by general656: 15 August 2017 - 02:34 PM
Reason for edit:: SO link removed.


Is This A Good Question/Topic? 0
  • +

Replies To: ReactJS+Mobx - How to create a Dynamic List?

#2 modi123_1  Icon User is online

  • Suitor #2
  • member icon



Reputation: 13488
  • View blog
  • Posts: 53,879
  • Joined: 12-June 08

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 15 August 2017 - 02:24 PM

Quote

If you can answer this question
Login in Stackoverflow to get points:
StackOverflow Question

Knock that off. We encourage people to keep their answers on the forum for everyone's benefit.
Was This Post Helpful? 0
  • +
  • -

#3 general656  Icon User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 287
  • Joined: 25-March 15

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 15 August 2017 - 02:25 PM

View Postmodi123_1, on 15 August 2017 - 02:24 PM, said:

Quote

If you can answer this question
Login in Stackoverflow to get points:
StackOverflow Question

Knock that off. We encourage people to keep their answers on the forum for everyone's benefit.

Alrighty! I just thought it would be great to give a reward to people.

This post has been edited by general656: 15 August 2017 - 02:27 PM

Was This Post Helpful? 0
  • +
  • -

#4 modi123_1  Icon User is online

  • Suitor #2
  • member icon



Reputation: 13488
  • View blog
  • Posts: 53,879
  • Joined: 12-June 08

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 15 August 2017 - 02:25 PM

Your video shows:

Quote

Private video: Log in to watch (if you have permission)

Was This Post Helpful? 0
  • +
  • -

#5 general656  Icon User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 287
  • Joined: 25-March 15

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 15 August 2017 - 02:27 PM

View Postmodi123_1, on 15 August 2017 - 02:25 PM, said:

Your video shows:

Quote

Private video: Log in to watch (if you have permission)


(I'll upload the video in Youtube for anyone who's doesn't have an account in Vimeo)


Do you have anything in mind though? I'm struggling with this crap.

This post has been edited by general656: 15 August 2017 - 02:36 PM

Was This Post Helpful? 0
  • +
  • -

#6 ArtificialSoldier  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1829
  • View blog
  • Posts: 5,758
  • Joined: 15-January 14

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 15 August 2017 - 03:40 PM

What code is supposed to remove the elements? The removeObject function has 1 line which removes it from the array, but what removes the HTML elements?
Was This Post Helpful? 0
  • +
  • -

#7 general656  Icon User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 287
  • Joined: 25-March 15

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 19 August 2017 - 06:47 AM

View PostArtificialSoldier, on 15 August 2017 - 03:40 PM, said:

What code is supposed to remove the elements? The removeObject function has 1 line which removes it from the array, but what removes the HTML elements?

This is ... ReactJS+MobX ... if I alter @observable or a state variable ... it synchronizes the reactjs html elements that connect with them.
Was This Post Helpful? 0
  • +
  • -

#8 ArtificialSoldier  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1829
  • View blog
  • Posts: 5,758
  • Joined: 15-January 14

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 21 August 2017 - 09:35 AM

So why is it removing the wrong element then? Did you mess up a connection somewhere? I noticed the title, but I don't work with either of those libraries, I don't like the React syntax at all.
Was This Post Helpful? 0
  • +
  • -

#9 general656  Icon User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 287
  • Joined: 25-March 15

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 21 August 2017 - 08:50 PM

View PostArtificialSoldier, on 21 August 2017 - 09:35 AM, said:

So why is it removing the wrong element then? Did you mess up a connection somewhere? I noticed the title, but I don't work with either of those libraries, I don't like the React syntax at all.

No you don't actually connect things the way I think you suppose.
If you want a list of names in React, which names are stored in a State Manager or in your class' state, then whenever a name in the state changes, it reloads the DOM updating the element that contains the list of names.

So in my case, if I removed an element, it would update the DOM without the element I just removed.

I fixed the problem though. Not exactly, unfortunately I don't have enough time at the moment to play with this issue to find what the actual problem was because it's for a Client. I just rewrote some code to fix fast the issue.

What do you use though? Angular I suppose?

This post has been edited by general656: 21 August 2017 - 08:52 PM

Was This Post Helpful? 0
  • +
  • -

#10 ArtificialSoldier  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1829
  • View blog
  • Posts: 5,758
  • Joined: 15-January 14

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 22 August 2017 - 09:37 AM

I use VanillaJS.

Although our major application does use jQuery and React for things, I just don't normally work on those parts.
Was This Post Helpful? 1
  • +
  • -

#11 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon


Reputation: 6979
  • View blog
  • Posts: 14,602
  • Joined: 16-October 07

Re: ReactJS+Mobx - How to create a Dynamic List?

Posted 25 August 2017 - 11:59 AM

I fiddled around with this a bit. I ultimately decided I have no idea why I'd want to use MobX. To be fair, I've come to the same conclusion about Redux and Flux and all the relatives.

I use TypeScript and tend to just wire this things up with a readonly state offered to all compontents and methods for change passed.

I thought I'd offer this, on the off chance it might be helpful.
import * as React from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AutoComplete from 'material-ui/AutoComplete';
import TextField from 'material-ui/TextField';
import { Panel, Col, Form } from 'react-bootstrap';
import FloatingActionButton from 'material-ui/FloatingActionButton';
import ContentAdd from 'material-ui/svg-icons/content/add';
import ContentRemove from 'material-ui/svg-icons/content/remove';


// each class gets a prperty definition
interface StaticItemProps {
  value: string;
  remove(): void;
  readonly floatingLabelText?: string;
}

// no state in this one
class StaticItem extends React.Component<StaticItemProps, {}> {
  render() {
    return (
      <div className="listItem">
        <Col sm={10}>
          <MuiThemeProvider>
            <TextField
              value={this.props.value}
              floatingLabelText={this.props.floatingLabelText}
              disabled={true}
              fullWidth={true}
            />
          </MuiThemeProvider>
        </Col>
        <Col sm={2}>
          <MuiThemeProvider>
            <FloatingActionButton
              mini={true}
              secondary={true}
              onclick={() => this.props.remove()}
              className="listButton"
            >
              <ContentRemove />
            </FloatingActionButton>
          </MuiThemeProvider>
        </Col>
      </div>
    );
  }
}


export interface AddItemProps {
  readonly autoCompleteData?: string[];
  readonly floatingLabelText?: string;
  addItem(name: string): void;
}

// state in this, as no need for activeName to polute the rest of the app
class AddItem extends React.Component<AddItemProps, { activeName: string }> {
  constructor(p) {
    super(p);
    this.state = { activeName: "" };
  }

  private addProduct() {
    this.setState((s, p) => {
      const name = s.activeName;
      p.addItem(name);
      s.activeName = "";
      return s;
    });
  }
  private updateActiveName(x: string) {
    this.setState((s, p) => {
      s.activeName = x;
      return s;
    });
  }

  render() {
    // console.log(["AddItem", this.state]);
    return (
      <div className="listAddItem">
        <Col sm={10}>
          <MuiThemeProvider>
            <AutoComplete
              searchText={this.state.activeName}
              onUpdateInput={x => this.updateActiveName(x)}
              floatingLabelText={this.props.floatingLabelText}
              dataSource={this.props.autoCompleteData}
              openonfocus={true}
              fullWidth={true}
              onkeypress={e => { if (e.key === "Enter") { this.addProduct(); } }}
            />
          </MuiThemeProvider>
        </Col>
        <Col sm={2}>
          <MuiThemeProvider>
            <FloatingActionButton
              mini={true}
              className="listButton"
              onclick={() => this.addProduct()}
            >
              <ContentAdd />
            </FloatingActionButton>
          </MuiThemeProvider>
        </Col>
      </div>
    );
  }
}

// the heart of the beast
// we want the object type of our collection to be generic
export interface ListInterfaceProps<T> {
  readonly id: string;
  readonly title: string;
  readonly items: T[];
  readonly autoCompleteData?: string[];
  readonly floatingLabelText?: string;
  getItemName(item: T): string;
  addItem(name: string): void;
  removeItem(item: T): void;
}

// so, no state here: we pass mutable calls up
export class ListInterface<T> extends React.Component<ListInterfaceProps<T>, {}> {
  render() {
    const { id } = this.props;
    return (
      <div id={id} className="listInterface">
        <Panel header={this.props.title}>
          <Form>
            {this.props.items
              .map((x, i) => <StaticItem
                key={i}
                value={this.props.getItemName(x)}
                floatingLabelText={this.props.floatingLabelText}
                remove={() => this.props.removeItem(x)}
              />)
            }
            <AddItem
              autoCompleteData={this.props.autoCompleteData}
              floatingLabelText={this.props.floatingLabelText}
              addItem={x => this.props.addItem(x)}
            />
          </Form>
        </Panel>
      </div>
    );
  }
}



Now, some driver code:
import * as React from 'react';
import { ListInterface, ListInterfaceProps } from './ListInterface';

export interface Product {
  id: number;
  name: string;
}

export class App extends React.Component<{}, { lastId: number; products: Product[]; }> {
  constructor(p) {
    super(p);
    this.state = {
      products: [{ name: "Apple", id: 900 }, { name: "Orange", id: 901 }],
      lastId: 1000
    };
  }

  private addProduct(name: string): void {
    this.setState((s, p) => {
      s.products.push({ name: name, id: ++s.lastId });
      console.log("Added Component!");
      console.log("Name: ", name);
      console.log("----------------------------\n");
      return s;
    });

  }

  private removeProduct(x: Product): void {
    this.setState((s, p) => {
      s.products = s.products.filter(y => y.id !== x.id);
      console.log(x);
      console.log(s.products);
      console.log("----------------------------\n");
      return s;
    });
  }

  render() {
    return (
      <ListInterface id="lstProducts"
        title="Products"
        items={this.state.products.slice(0)}
        autoCompleteData={["Apostolis", "Orestis", "Germanos", "Kwnstantina", "Tasos"]}
        floatingLabelText="Product"
        getItemName={(x:Product) => x.name}
        addItem={x => this.addProduct(x)}
        removeItem={(x:Product) => this.removeProduct(x)}
      />
    );
  }
}


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1