christiancn7
christiancn7

Reputation: 175

React should i keep a function in local state

Never really done something like this before and couldn't find much information about it but is it bad to store a function reference in a component's local state?

class Foo extends React.Component{
    this.state = {
      exampleFunction: () => {}
    }
}

It'll get passed down into another component where that component can change the functionality of this function.

// bar.js component

handleChange = (newFunction) => {
  const { exampleFunction } = this.props;
  this.setState({ exampleFunction: newFunction });
}

If this is bad, what should be done instead? This isn't redux store but just react local state.

Upvotes: 3

Views: 11324

Answers (2)

Abdul Ahmad
Abdul Ahmad

Reputation: 10021

I was just thinking about what you're trying, and I don't think it's actually doing what you think. When you pass down the function to the child, and you call this.setState it's not setting the parent component's state, it's changing the child component's state. So you're not really changing the function. Also, props are read-only, so I don't think you can reassign them.

class Parent extends Component {
  state = { visible: true };

  render() {
    const { visible } = this.state;
    return <Child visible={visible} />;
  }
}

class Child extends Component {
  // props.visible = true
  onChange = () => {
    this.setState({ visible: false });
    // props.visible is still true here, child has it's own state
  }
}

the code above demonstrates this, when onChange is called in the child, the props.visible coming from the parent will still be true, because the child component has it's own state. So the same thing will happen with your function. There is a way you can do this though, look at the code below:

class Parent extends Component {
  dynamicFunction = () => {};

  setDynamicFunction(newFunc) {
    this.dynamicFunction = newFunc;
  }

  render() {
    return <Child setDynamicFunction={this.setDynamicFunction} />;
  }
}

class Child extends Component {
  onSomeEvent = () => {
    this.props.setDynamicFunction(newFunction);
  }
}

if you want to just update the parent's dynamic function and only use it in the parent, you can just do what I did above, but if you want the child to be aware of when the dynamic function has changed, you'll need to move the dynamic function from the class into the state, this way, when you change the dynamic function, it will cause the child to re-render and it will get the newly set dynamic function passed down as a prop

Upvotes: 2

dance2die
dance2die

Reputation: 36915

If you are passing your method to deeply nested components, you might want to look into React Context API.
Refer to article, Updating Context from a Nested Component.

Using Redux just for the sake of drilling methods into subcomponents is an overkill.

In fact, the React documentation passes a method down to child components.
below is the code copied from the documentation mentioned

Create a context with an empty implementation of toggleTheme.

export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},
});

Then the consumer is passed toggleTheme, which is used by a button.

import {ThemeContext} from './theme-context';

function ThemeTogglerButton() {
  // The Theme Toggler Button receives not only the theme
  // but also a toggleTheme function from the context
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button
          onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

And the provider (it's the one that passes the implementation of toggleTheme) makes toggleTheme available to context consumers.

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => { ... };

    // State also contains the updater function so it will
    // be passed down into the context provider
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,
    };
  }

  render() {
    // The entire state is passed to the provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}

<ThemeContext.Provider value={this.state}> that is where you provide the implementation of toggleTheme.

Upvotes: 4

Related Questions