0

I'm trying to figure out the best place to manage the state for lists of input in react-native but haven't found any good, thorough examples or clear guidance and I can see a few options.

For simplicity not including specifics about the tool for managing state, as my understanding is how the state is stored doesn't impact the component where it's managed.

Scenario

A screen component that receives an array of items as props to be displayed in a List of ListItems. Each ListItem includes a input, for simplicity imagine a switch (boolean).

Use cases include an array of form questions or settings to be displayed in a list and allowing user input. Pseudocode:

class SettingsView extends Component {
  render () {
    return (
      <View>
        <List style={styles.list}>
          {this.props.inputArray.map((item, index) => (
            <ListItem
              title={item.title}
              isSwitched={item.value}
              key={index}
              onSwitchChange = {this.onChange}
            />
          ))}
        </List>
      </View>
    );
  }
}

Option 1 Based on the Thinking in React page, one option that comes to mind is managing state at the Screen (SettingsView) level by creating an array of (inputArray).length in the SettingsView constructor state and have the onChange function update that array based on key.

Option 2 The second option I see is having each ListItem manage the state it's displaying. This makes sense from an encapsulation perspective to me, but then less so for managing of the state, given that the onSwitchChange function is in the SettingsView and I'm not as clear how this would work.

Are there other options I'm not considering? Admit that experience in React/RN is limited and def coming from a more object mindset like iOS's list datasource model.

Note: Another option is having the entire inputArray in state, instead of passed as props. My understanding is that state should be minimized, so was designing that only the inputs to each item in inputArray should be in the state. i.e. Form Labels (i.e. questions) are props not state.

1 Answer 1

1

Option 1 would be the better choice, there is this concept "Smart Components and Dumb Components"

Smart Components: typically holds the state of all the child components associated with it, it also defines the functions that is passed down to child components to modify its state.

Dumb Components: These are components that receives props which includes data and functions they typically don't have their own state and relies heavily on the parent component.

The problem is that when you create a component you need to decide whether it's smart or dumb, usually I associate a screen to a smart component, in your example it would be the SettingsView(smart) that will hold the state and function and it's children will be the dumb components but this is really application specific decision because you might have a SettingsView that are dynamic based on context and so it would be much better to make it a dumb component let's use your example above

Since Settings View relies on this.props.inputArray passed from a parent component(I will call this ParentComponent) you couldn't modify inputArray directly in SettingsView what you could do is pass another prop from ParentComponent to SettingsView which is a function that modifies inputArray

class ParentComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputArray: [],
        };
        this.onSwitchChange = this.onSwitchChange.bind(this); // when passing fn
    }
    onSwitchChange(index) { // index will come from the child component
        // do some logic here based on the index then update the state
        this.setState('inputArray' updatedState); // updatedState is just an example variable
    }
    render() {
        return (
            <View>
                <SettingsView 
                    onSwitchChange={(index) => () => this.onSwitchChange(index)}
                    inputArray={this.state.inputArray} 
                />
            </View>
        );
    }
    /*
        (index) => () => this.onSwitchChange(index)
        this is a function that will return another function
        we need this because we want to delay the execution of onSwitchChange
        and capture index and associate it with that method, typically this
        is used if were passing function to child component(SettingsView) which will be used as
        a handler for events.
    */
}

class SettingsView extends Component {
  render () {
    return (
      <View>
        <List style={styles.list}>
          {this.props.inputArray.map((item, index) => (
            <ListItem
              title={item.title}
              isSwitched={item.value}
              key={index}
              onSwitchChange={this.props.onSwitchChange}
            />
          ))}
        </List>
      </View>
    );
  }
}

This example might be pointless because you could just use SettingsView as the parent of ListItem and other components but since SettingsView state is now managed by ParentComponent it is now a dumb component and can be used in other screens that have the specific state that SettingsView needs to operate. the general goal is to create as many dumb components as possible the reason being is that these type of components are highly reusable because you just need to pass them props to properly work.

Sign up to request clarification or add additional context in comments.

1 Comment

The example is only off by a level, I was debating between ListItem being 'Smart' or 'Dumb'. ParentComponent adds a third level, but the concepts still hold. Great answer, I'll put the state in the parent.

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.