How to make variables available to every method in a component?

Viewed 806

Let's say I am mapping my store state to props like so:

const mapStateToProps = state => {
    return {
        somelist: state.somelist
    };
};

And I need to create variables in my render like:

render() {
  const { somelist } = this.props;
  const somenestedlist = somelist.list;
}

However, in my render method I am also calling another method that needs those same variables. So I can either:

(1) Pass it from the render method like:

render() {
  const { somelist } = this.props;
  const somenestedlist = somelist.list;

  <div onClick={()=>this.someothermethod(somenestedlist)}
}

(2) Redeclare them in the someothermethod() which is not DRY.

(3) Create a function that returns the needed value and call it wherever it's needed as in:

getList() {
  const { somelist } = this.props;
  const somenestedlist = somelist.list;

  return somenestedlist;
}

OR is there a better way than any of these examples?

5 Answers

You can convert somenestedlist in mapStateToProps:

const mapStateToProps = state => {
    return {
        somelist: state.somelist,
        somenestedlist: state.somelist.list
    };
};

In everywhere, you can use this.props.somenestedlist

If you don't need to alter the list, then this.props.someList is accessible from within any of the class' methods.

However, if you want to add/remove items from this list from within the component without changing someList, then the simplest solution is to declare a nestedList variable from the someList source in the class constructor (the example below, where state = {...}, is syntactic sugar for the same thing), and then utilize React state to alter this new list.

Now, you can either discard the new list or call a Redux action creator to save and update the props list (consequently, you can also reset both lists as long as the initial list source stays constant).

Working example: https://codesandbox.io/s/6w5r5o32qk

import React, { Component, Fragment } from "react";
import { connect } from "react-redux";
import { resetList, saveList } from "./listActions";

const newData = [
  {
    _id: "5b9c3b351e5f7d9b827df4ce",
    index: 0,
    guid: "048e6f79-c33c-42fc-83e3-05f5b467240d",
    isActive: false,
    balance: "$1,663.79",
    picture: "http://placehold.it/32x32",
    age: 23,
    name: "Tonya Drake"
  },
  {
    _id: "5b9c3b350e7b14d4c94043f2",
    index: 1,
    guid: "1e3daeeb-36fd-4e52-a30e-5dbaedec8438",
    isActive: true,
    balance: "$2,263.69",
    picture: "http://placehold.it/32x32",
    age: 40,
    name: "Patricia Phelps"
  }
];

class Example extends Component {
  state = {
    addButtonClicked: false,
    saveButtonClicked: false,
    nestedList: this.props.someList
  };

  componentDidUpdate = prevProps => {
    if (prevProps.someList !== this.props.someList) {
      this.setState({
        nestedList: this.props.someList
      });
    }
  };

  addData = () => {
    this.setState(prevState => ({
      addButtonClicked: true,
      nestedList: [...this.state.nestedList, newData]
    }));
  };

  resetData = () => {
    this.setState(prevState => {
      this.props.resetList();
      return {
        addButtonClicked: false,
        saveButtonClicked: false,
        nestedList: this.props.someList
      };
    });
  };

  saveNewList = () =>
    this.setState(prevState => {
      this.props.saveList(this.state.nestedList);
      return { saveButtonClicked: true };
    });

  render = () => (
    <div style={{ textAlign: "center" }}>
      <h1>Utilizing React State</h1>
      <button
        className="uk-button uk-button-primary uk-button-large"
        type="button"
        onClick={this.addData}
        disabled={this.state.addButtonClicked || this.state.saveButtonClicked}
        style={{ marginRight: 20 }}
      >
        Add Data
      </button>
      <button
        type="button"
        className="uk-button uk-button-danger uk-button-large"
        onClick={this.resetData}
        disabled={!this.state.addButtonClicked && !this.state.saveButtonClicked}
        style={{ marginRight: 20 }}
      >
        Reset List
      </button>
      <button
        type="button"
        className="uk-button uk-button-secondary uk-button-large"
        onClick={this.saveNewList}
        disabled={
          (this.state.addButtonClicked && this.state.saveButtonClicked) ||
          (!this.state.addButtonClicked && !this.state.saveButtonClicked)
        }
      >
        Save List
      </button>
      <pre style={{ height: 600, overflowY: "auto", textAlign: "left" }}>
        State List&nbsp;
        <code>{JSON.stringify(this.state.nestedList, null, "\t")}</code>
        <br />
        <br />
        Props List&nbsp;
        <code>{JSON.stringify(this.props.someList, null, "\t")}</code>
      </pre>
    </div>
  );
}

export default connect(
  state => ({ someList: state.list }),
  { resetList, saveList }
)(Example);

If you don't want to redeclare variables in every function you use, then... I think your only option is to use the value directly from the props...

Then, do this:

yourMethod(){
    //Do somethign with data directly, no need to declare a const again
    this.anotherMethod(this.props.somelist.list);
}

instead of:

const { somelist } = this.props;
const somenestedlist = somelist.list;

I mean, the values ARE already available to all methods, just use them... const { somelist } = this.props; is only used as a helper to not write this.props.somelist all over the method.

if you mean that you want to use a variable in specific component you can use these options :

1.use props

2.use state

3.declare a variable inside your component

4.use store for component and declare variable inside store and pass the store to your component

5.....

if you mean that you want to use a specific variable in multiple components you can use these options :

1.declare parent or base component and declare variable inside it and pass it via props to child component

2.you can use specific store for multiple component and declare variable inside it

3....

but be careful for using variable in complex situation, it can easy reduce your project performance by wrong usage

also you can use observable attribute to change and order to re-render automatically

You can construct somelist and somelist.list into an object in mapStateToProps and return the object. Something like below

const mapStateToProps = state => {
    const obj = {};
    obj.somelist = state.somelist;
    obj.someNestedList = state.somelist.list;
    return {
        list: obj
    };
};

And in render you can get somelist and someNestedList from list props object without assigning to a local variable. Something like below

 someothermethod = list => {
      console.log(list.someList);
      console.log("someNestedList", list.someNestedList);
 }
 render() {
      const { list } = this.props;
      console.log("somelist", list.somelist);
      console.log("someNestedList", list.someNestedList);
      return(
         <div onClick={()=>this.someothermethod(list)}></div>
      )
    }
Related