Reputation: 175
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
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
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