13

Considering this pseudocode:

component.js

...
import {someFunc} from "./common_functions.js"

export default class MyComp extends Component {
    constructor(props) {
        super(props);

    this.someFunc = someFunc.bind(this);

    this.state = {...};
    }

    _anotherFunc = () = > {
        ....
        this.someFunc();
    }

    render() {
        ...
    }
}

common_functions.js

export function someFunc() {
    if(this.state.whatever) {...}
    this.setState{...}
}

How would I bind the function someFunc() to the context of the Component? I use it in various Components, so it makes sense to collect them in one file. Right now, I get the error "Cannot read whatever of undefined". The context of this is unknown...

8
  • Have you tried remove export in your someFunc, and put this function inside you MyComp class? Commented Aug 15, 2017 at 7:27
  • @Jacky of course that would work. But I use someFunc() in several Components... Commented Aug 15, 2017 at 7:28
  • 1
    Maybe you could use HOC to manage your state, and pass the props to your wrapped compoent. facebook.github.io/react/docs/… Commented Aug 15, 2017 at 7:36
  • 1
    If you need to manage the state in a centralized way, then probably you should use store for managing that state. You can make your common function as an action and trigger from any component. This would be the best way to handle things in React. Commented Aug 15, 2017 at 7:43
  • @Fawaz the reason why i dont wanna use redux is because the updating from the local state in the component is only for styling. To me, using redux only for styling of a component does not sounds reasonable... Commented Aug 15, 2017 at 8:35

8 Answers 8

5

You can't setState outside of the component because it is component's local state. If you need to update state which is shared, create a store (redux store).

In your case, you can define someFunction at one place and pass it the specific state variable(s) or entire state. After you are done in someFunction, return the modified state and update it back in your component using setState.

export function someFunc(state) {
    if(state.whatever) {...}
    const newState = { ...state, newValue: whateverValue }
    return newState
}

_anotherFunc = () = > {
        ....
        const newState = this.someFunc(this.state);
       this.setState({newValue: newState});
    }
Sign up to request clarification or add additional context in comments.

1 Comment

the reason why i dont wanna use redux is because the updating from the local state in the component is only for styling. To me, using redux only for styling of a component does not sounds reasonable...
3

it's not a React practice and it may cause lot of problems/bugs, but js allows to do it:

Module A:

    export function your_external_func(thisObj, name, val) {
       thisObj.setSate((prevState) => { // prevState - previous state 
         // do something with prevState ...

         const newState = { // new state object
           someData: `This is updated data ${ val }`, 
           [name]: val,
         };
         return newState 
       });
    }

Then use it in your react-app module:

import { your_external_func } from '.../your_file_with_functions';

class YourReactComponent extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state={
      someName: '',
      someData: '',
    };
  }

  handleChange = (e) => {
    const { target } = event;
    const { name } = target;
    const value = target.type === 'checkbox' ? target.checked : target.value;

    your_external_func(this, name, value);
  }

  render() {
    return (<span>
      { this.state.someData }
      <br />
      <input 
        name='someName' 
        value={ this.state.someName }
        onChange={ this.handleChange }
      />
   </span>);
  }
}

It's a stupid example :) just to show you how you can do it

1 Comment

This is the only way I found too to update the state from a reusable function, if I just returned the result from the function and then updated the state it never worked...
1

The best would obviously to use some kind of external library that manages this. As others have suggested, Redux and MobX are good for this. Using a high-order component to wrap all your other components is also an option.

However, here's an alternative solution to the ones above:


You could use a standard javascript class (not a React component) and pass in this to the function that you are calling from that class.

It's rather simple. I've created a simple example below where the state is changed from a function of another class; take a look:

class MyApp extends React.Component {

  constructor() {
    super();
    this.state = {number: 1};
  }

  double = () => {
    Global.myFunc(this);
  }

  render() {
    return (
      <div>
        <p>{this.state.number}</p>
        <button onClick={this.double}>Double up!</button>
      </div>
    );
  }
}

class Global {
  static myFunc = (t) => {
    t.setState({number: t.state.number*2});
  }
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"><div>

5 Comments

the reason why i dont wanna use redux is because the updating from the local state in the component is only for styling. To me, using redux only for styling of a component does not sounds reasonable...
@Stophface, okay. This isn't a redux-solution btw, not sure if you noticed. :)
I noticed. Put you also pointed me to redux, thats why I added :)
@Stophface, alright. Did any of the answers help you?
This doesn't seem to work if the global function is imported from another file
1

There is a functional form of setState that can even be used outside of a component.

This is possible since the signature of setState is:

* @param {object|function} partialState Next partial state or function to
*        produce next partial state to be merged with current state.
* @param {?function} callback Called after state is updated.

See Dan's tweet: https://twitter.com/dan_abramov/status/824308413559668744

1 Comment

Second time I've seen this twitter post and it no longer exists. Ugh. This is why it's nice to have complete answers here.
0

This all depends on what you are trying to achieve. At first glance I can see 2 options for you. One create a child component and two: use redux as redux offers a singular state between all of your child components.

First option:

export default class parentClass extends Component {
    state = {
        param1: "hello".
    };

    render() {
        return (
            <Child param1={this.state.param1}/>
        );
    }
}
class Child extends Component {
    render() {
        console.log(this.props.param1);
        return (
            <h1>{this.props.param1}</h1>
        );
    }
}

Now the above child component will have the props.param1 defined from the props passed from it's parent render function.

The above would work but I can see you're trying to establish a 'common' set of functions. Option 2 sort of provides a way of doing that by creating a singular state for your app/project. If you've haven't used redux before it's pretty simple to use once you've got the hang of it. I'll skip out the setup for now http://redux.js.org/docs/basics/UsageWithReact.html.

Make a reducer like so:

import * as config from './config';//I like to make a config file so it's easier to dispatch my actions etc
//const config.state = {param1: null}
//const config.SOME_FUNC = "test/SOME_FUNC";

export default function reducer(state = config.state, action = {}) {
    switch(action.type) {
        case config.SOME_FUNC:
            return Object.assign({}, state, {
                param1: action.param1,
            });
        break;
        default:
            return state;
        }
    }
}

Add that to your reducers for your store.

Wrap all your components in the Provider.

ReactDOM.render(
    <Provider store={store} key="provider">
        <App>
    </Provider>,
    element
);

Now you'll be able to use redux connect on all of the child components of the provider!

Like so:

import React, {Component} from 'react';
import {connect} from 'react-redux';

@connect(
    state => (state),
    dispatch => ({
        someFunc: (param1) => dispatch({type: config.SOME_FUNC, param1: param1}),
    })
)
export default class Child extends Component {

    eventFunction = (event) => {
        //if you wanted to update the store with a value from an input
        this.props.someFunc(event.target.value);
    }

    render() {
        return (
            <h1>{this.props.test.param1}</h1>
        );
    }
}

When you get used to redux check this out https://github.com/redux-saga/redux-saga. This is your end goal! Sagas are great! If you get stuck let me know!

2 Comments

the reason why i dont wanna use redux is because the updating from the local state in the component is only for styling. To me, using redux only for styling of a component does not sounds reasonable...
That's fair enough. Well then you could pass props to a child or pass params to your someFunc function someFunc(params)
0

Parent component example where you define your callback and manage a global state :

export default class Parent extends Component {

    constructor() {
      super();
      this.state = {
        applyGlobalCss: false,
      };
    }
    
    toggleCss() {
      this.setState({ applyGlobalCss: !this.state.applyGlobalCss });
    }

    render() {
        return (
            <Child css={this.state.applyGlobalCss} onToggle={this.toggleCss} />
        );
    }
}

and then in child component you can use the props and callback like :

export default class Child extends Component {

    render() {
        console.log(this.props.css);
        return (
            <div onClick={this.props.onToggle}>
            </div>
        );
    }
}

Child.propTypes = {
  onToggle: PropTypes.func,
  css: PropTypes.bool,
};

Comments

0

Well for your example I can see you can do this in a simpler way rather than passing anything.

Since you want to update the value of the state you can just return it from the function itself.

Just make the function you are using in your component async and wait for the function to return a value and set the state to that value.

import React from "react"

class MyApp extends React.Component {

  constructor() {
    super();
    this.state = {number: 1};
  }

  theOnlyFunction = async() => {
     const value = await someFunctionFromFile( // Pass Parameters );

     if( value !== false )  // Just for your understanding I am writing this way
     {
        this.setState({ number: value })
     }
  }

  render() {
    return (
      <div>
        <p>{this.state.number}</p>
        <button onClick={this.double}>Double up!</button>
      </div>
    );
  }
}


And in SomeOtherFile.js

function someFunctionFromFile ( // catch params) {
  if( //nah don't wanna do anything )  return false;

  // and the blahh blahh algorithm
}

Comments

0

you should use react Context

Context lets us pass a value deep into the component tree without explicitly threading it through every component. here is a use case from react docs : create a context for the current theme (with "light" as the default).

const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // Use a Provider to pass the current theme to the tree below.
    // Any component can read it, no matter how deep it is.
    // In this example, we're passing "dark" as the current value.
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // Assign a contextType to read the current theme context.
  // React will find the closest theme Provider above and use its value.
  // In this example, the current theme is "dark".
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

resource: https://reactjs.org/docs/context.html

Comments

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.