Issue: I am using React with Redux store to manage my state. I want to display filters on a component. These filters will be returned from Tableau Javascript API. Based on the return from Tableau JavaScriptAPI I am updating the state in the store. The state is getting updated correctly with the right filters, which is also causing my component to update/ re-render. All good so far.
But when I am trying to access these filters (be it object using Object.keys() or array using array.length) from state.props, I am getting null array in return.
This is probably dues to incorrect use of Thunk/ Async Await.
Here is the code snippet
Step 1: Calling the Action from React. When I click a button, fetch dashboard action is called
class DashboardView extends Component {
componentDidMount() {
id = this.props.dashboardDetails.categoryID;
console.log("Mounted");
this.props.fetchDashboard(
this.props.dashboardDetails.categoryURL,
this.vizContainer
);
}
Step 2A: Here's the fetchDashboard action: within this action, I am creating filterNAmes array which I want to display on my component. I am making sing of Thunk here as my actions sends an API request to Tableau JavascriptAPI. The way I want it to work is once, we get response from the API, we will dispatch the viz to a reducer and the filterNames array to another action fetchFilter. Snippet attached.
export const fetchDashboard = (url, vizContainer) => async dispatch => {
let filterNames = [];
// let titlesTemp = {};
//const vizContainer = {};
const options = {
hideToolbar: true,
onFirstInteractive: function() {
let workbook = viz.getWorkbook();
const activeSheet = workbook.getActiveSheet();
activeSheet
.getWorksheets()
.get("Index")
.getFiltersAsync()
.then(function(filters) {
for (let filter in filters) {
let filterTitle = filters[filter].getFieldName();
let filterName = {};
filterName[filterTitle] = [];
let len = filters[filter].getAppliedValues().length;
for (let i = 0; i < len; i++) {
filterName[filterTitle].push(
filters[filter].getAppliedValues()[i].value
);
}
filterNames.push(filterName);
}
});
}
};
let viz = await new window.tableau.Viz(vizContainer, url, options);
dispatch({ type: "FETCH_DASHBOARD", payload: { viz } });
console.log(filterNames, filterNames.length);
return dispatch(fetchFilter(filterNames));
};
When I console.log(filterNames, filterNames.length), the filterNames array has elements in it(which get evaluated later) but the filterNames.length return 0. I think this is the root cause of the issue. I want to dispatch the next action only when FilterNames is populated.
Step 2B:
export const fetchFilter = filterNames => {
console.log("FETCH_FILTER action called", filterNames);
return { type: "FETCH_FILTER", filterNames };
};
Assuming this is not the issue- Step 3: Reducer
const fetchFilterReducer = (fetchFilter = [], action) => {
if (action.type === "FETCH_FILTER") {
console.log("fetchfilterreducercalled:", action.filterNames);
return action.filterNames;
}
return fetchFilter;
};
This updates the State fetchFilter in the store correctly.
Step 4: Using the connect function, I am pulling the fetchFilter array in the property of the component.
class FilterDisplay extends React.Component {
componentDidMount() {
console.log(this.props);
if (this.props.selectionFilters.length !== 0) {
console.log(this.props);
}
}
componentDidUpdate() {
console.log(this.props.selectionFilters.length);
if (this.props.selectionFilters.length !== 0) {
console.log("filter display updated");
console.log(this.props);
}
}
render() {
return <div>hey</div>; //<Tile></Tile>;
}
}
const mapStateToProps = state => {
console.log("first filters in state:", state.fetchFilter);
return {
selectionFilters: state.fetchFilter
};
};
export default connect(mapStateToProps)(FilterDisplay);
On my componentDidUpdate, I am getting length of selectionFilters array as 0. This is not allowing me to display the contents of the array on my screen.
This is a lot to read but I could not fit it in a smaller description. Please let me know in case I need to add more details.
Thanks for all the help in advance :)