0
votes

I have a problem while incorporating the redux saga in my application . What I understand from the tutorials is that the middleware will parse the action dispatched from the app and will process some async operations(asycn storage, api).

Then put another action in the pipeline which will trigget the reducer and then updates the state.

Flow is that my component button click triggers an action dispatch which is caught by the watcher in the saga, and then api calls are processed then the put is done to send data to reducer to update the state.

If in my component I dispatch the action FETCH_DATA, the saga is catching that and processing the data fetch and then calling the updateprofile. This calls the reducer and processing happens.

This is what I expected. But even before hitting the saga FETCH_DATA, the call comes to the reducer and since there is no action type FETCH_DATA case to handle it will return the default state and thiscauses the app to re render. So rendering happens twice and it is causing some problem to my items in the list.

I think this is also somewhat expected as I read in some article .How to get rid of this re rendering ?

function* datafetch(action) {
 let { data } = yield call(loginApi, action.payload);
  yield put(updateProfile(data.profile));
}

export function* dataFetcherSaga() {
  yield takeLatest('FETCH_DATA', datafetch);
}

/reducer.js
const toDoListReducer = (state, action) => {
  switch (action.type) {
    case "UPDATE_APPREDUX_STATE":
      return {
        //some processing with data and state
      };
      break;
    case "UPDATE_PROFILE":
      return {
        //some processing with data and state
      };
      break;

    default:
      return state;
  }

  return state;
};
export default toDoListReducer;



//action
export const fetchData = currentDay => {
  return {
    type: 'FETCH_DATA',
    currentDay: currentDay
  };
};

export function updateProfile(profile) {
  return { type: 'UPDATE_PROFILE', payload: authParams };
}

//component

render(){
return (
  <View style={styles.viewStyle}>

      <SafeAreaView>
        <View style={styles.viewPadding}>
          <View>
            <View style={styles.toDoViewStyle}>
              <TextInput
                style={styles.toDoInputStyle}
                placeholder="What you gonna do ?"
                onChangeText={text => {
                  this.newTask = text;
                }}
              />
              <TouchableOpacity
                onPress={() => {
                  this.props.updateTasks(
                    {
                      taskID: new Date().getTime(),
                      taskDay: this.currentDay, //millisecond field to get a unique value
                      taskValue: this.newTask,
                      taskCompleted: false,
                      taskCompletedTime: null
                    },
                    "addTask"
                  );
                }}
              >
                <Image
                  style={styles.addImage}
                  source={require("../../assets/add.png")}
                />
              </TouchableOpacity>
            </View>
            <Text>


              ↓ To Do Items ↓
            </Text>
            <SectionList
              style={styles.flatListStyle}
              renderItem={({ item }) => <ToDoListItem value={item} />}
              renderSectionHeader={({ section: { title, data } }) => {
                if (data.length > 0) {
                  return (
                    <Text
                      style={{
                        paddingTop: 5,
                        fontWeight: "bold",
                        fontStyle: "italic",
                        fontSize: 15,
                        color: title === "Completed Tasks:" ? "green" : "red",
                        textDecorationLine: "underline"
                      }}
                    >
                      {title}
                    </Text>
                  );
                }
              }}
              stickySectionHeadersEnabled={false}
              sections={[
                {
                  title: "Completed Tasks:",
                  data: this.props.tasks.filter(tasks => {
                    return tasks.taskCompleted === true;
                  })
                },
                {
                  title: "InComplete Tasks:",
                  data: this.props.tasks.filter(tasks => {
                    return tasks.taskCompleted === false;
                  })
                },
                ,
              ]}
              keyExtractor={(item, index) => item + index}
            />
          </View>
        </View>
      </SafeAreaView>

  </View>
);}

//child item

class ToDoListItem extends React.Component {


    constructor(props) {
        super(props);
        //this.state = { checked: false };
    }

    selectItem = () => {

        let updatedObject = {
            taskID: this.props.value.taskID,
            taskCompleted: !this.props.value.taskCompleted,
        };
        this.props.done(updatedObject);
    };
    deleteItem = () => {

        let deletedObject = {
            taskID: this.props.value.taskID,
        };
        this.props.delete(deletedObject);
    };
    render() {
        return (
            <View style={styles.viewStyle}>
                <View style={styles.checkBoxStyle}>
                  <CheckBox
                     checkedCheckBoxColor="green"
                        onClick={this.selectItem}
                        isChecked={this.props.value.taskCompleted}/>
                </View>
                <View style={styles.inputTextViewStyle}>
                    <Text
                        style={
                            this.props.value.taskCompleted
                                ? styles.completeDone
                                : styles.inComplete
                        }>
                        {this.props.value.taskValue}
                    </Text>

                    {this.props.value.taskCompleted && <Text style={{ fontSize: 11, fontStyle: "italic", color: "blue" }}>{"\n"}{"Completed @ " + this.props.value.taskCompletedTime}</Text>}

                </View>
                <View style={styles.deleteTextStyle}>
                    <TouchableOpacity onPress={this.deleteItem}>
                        <Image
                            style={styles.deleteImage}
                            source={require('../../assets/delete.png')}
                        />
                    </TouchableOpacity>
                </View>
            </View>
        );
    }
}
2
show a loading screen while fetching data. it is expected since initially the store doesn't have any data.Joseph D.
Thanks for looking into this issue I tried that showing a loading screen but it comes and goes in a fraction of second and I am using a section list to add the items from the this.props.data which is set using the mapStatetoProps(this data is coming from saga which puts a an action to update the state from reducer) . I see the list blinks for a fraction of second.rahulmr
please add your component.Joseph D.
I have added the component. I am new to this react native .If possible can you help me out whether something is wrong with the approach or the child item .rahulmr

2 Answers

2
votes

Yes this is expected behavior when you dispatch any action it will go to reducer first instead of any middleware. Best practice is to show the loading screen as mentioned but if you really want to hold your render then you can use shouldComponentUpdate() Component Lifecycle Method which takes two params nextProps and nextState. This method basically hold the re-render which can be achieved by a simple if condition for e.g.

shouldComponentUpdate (nextProps, nextState) {
    let shouldUpdate = true
    if ((nextProps.someProps === this.props.someProps )) {
        shouldUpdate = false
    }
    return shouldUpdate
}
1
votes

To prevent re-rendering, you could create a container for the component. In this container component, use the redux compose method and connect method from react-redux, e.g

const ContainerComponent = compose(
  connect(mapStateToProps),
  component-that-displays-on-loading
)(Component-that-needs-data-from-the-state);

compose is a higher order component that runs passed-in functions from right-to-left. This pattern skips the first render and executes the flow as expected. Hope this helps.