React state is not changing after the props change - reactjs

I'm using redux with react, In my react state I'm assigning some state properties to the props but in my state the accounts are changing but the loading property doesn't change, I also tried to update the state in componentdidupdate but when I try to do that I get maximum update depth exceeded
In AllAccounts the accounts property is updating when the accounts are done fetching and the props change but the same doesn't happen for the loading property, It is set to the first value when it changes when it is set to true, The prop value is changing for loading but it is not changing in the state
This is my component
import React, { Component } from "react";
import { connect } from "react-redux";
import {
getAccounts,
deleteAccounts,
addAccount,
} from "../../../actions/accountActions";
import { getUsersForTenant } from "../../../actions/userActions";
import { Drawer } from "antd";
import getSortUrl from "../../../utils/getSortUrl";
import "../tablepage.css";
import Table from "../../../components/Table/Table";
import SearchHeader from "../../../components/SearchHeader/SearchHeader";
import Loading from "../../../components/Loading/Loading";
import AddAccount from "../../../components/AddEntities/AddAccount/AddAccount";
class AllAccounts extends Component {
constructor(props) {
super(props);
this.fetchData = this.fetchData.bind(this);
this.setSort = this.setSort.bind(this);
this.deleteRows = this.deleteRows.bind(this);
this.toggleDrawer = this.toggleDrawer.bind(this);
this.addAccount = this.addAccount.bind(this);
}
state = {
page: 0,
size: 50,
loading: this.props.loading,
accounts: this.props.accounts,
sort: undefined,
selectedItems: [],
checkboxActive: false,
drawerVisible: false,
columns: [
{
key: "name",
title: "Name",
enabled: true,
searchable: true,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: true,
},
{
key: "companyDomain",
title: "Company Domain",
enabled: true,
searchable: true,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "industrySector.name",
title: "Industry Sector",
enabled: true,
searchable: true,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "picture",
title: "Picture",
enabled: false,
searchable: false,
type: "picture",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "nbrEmployees",
title: "No. Employees",
enabled: true,
searchable: true,
type: "number",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "fixedPhone",
title: "Phone",
enabled: true,
searchable: true,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "source.name",
title: "Source",
enabled: false,
searchable: true,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "linkedIn",
title: "LinkedIn",
enabled: true,
searchable: false,
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "social",
title: "Social Networks",
title: "Social Networks",
enabled: true,
searchable: false,
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "twitterPage",
title: "Twitter",
title: "twitter",
enabled: false,
bindCol: "social",
bindColTitle: "Social",
searchable: false,
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "instagramPage",
title: "Instagram",
enabled: false,
searchable: false,
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "googlePlus",
title: "Google Plus",
enabled: false,
searchable: false,
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "user",
title: "User",
enabled: false,
searchable: false,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "isCustomer",
title: "Is Customer",
enabled: false,
searchable: false,
type: "boolean",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
{
key: "Tags",
title: "Tags",
enabled: true,
searchable: false,
type: "string",
selected: false,
sortActive: false,
sortOrder: "ASC",
showCheckbox: false,
},
],
};
toggleDrawer = () => {
this.setState({
drawerVisible: !this.state.drawerVisible,
});
};
deleteRows = (rows) => {
if (rows) {
this.props.deleteAccounts(rows, 1);
this.setState({
loading: true,
});
} else {
const { selectedItems } = this.state;
this.props.deleteAccounts(selectedItems, 1);
this.setState({
selectedItems: [],
loading: true,
});
}
};
addAccount = (accountData) => {
this.props.addAccount(accountData, 1);
};
componentDidUpdate(prevState, prevProps, snap) {
// if (prevProps.accounts != this.props.accounts) {
// this.setState({
// accounts: this.props.accounts,
// loading: false,
// });
// }
// if (prevProps.users != this.props.users) {
// this.setState({
// assignableUsers: this.props.users,
// });
// }
}
setSort = (column) => {
this.setState({
sort: column,
});
};
selectRow = (select, row) => {
if (select) {
this.setState({
selectedItems: [...this.state.selectedItems, row],
checkboxActive: true,
});
} else {
var { selectedItems } = this.state;
var index = 0;
for (var i = 0; i < selectedItems.length; i++) {
if (selectedItems[i].id == row.id) {
console.log(i);
index = i;
}
}
selectedItems.splice(index, 1);
if (selectedItems.length == 0) {
this.setState({
selectedItems: selectedItems,
checkboxActive: false,
});
} else {
this.setState({
selectedItems: selectedItems,
});
}
}
};
fetchData = () => {
this.setState(
{
loading: true,
},
() => {
const { page, size, sort } = this.state;
var sortUrl = "";
if (sort) {
const sortKey = sort.key;
const sortOrder = sort.sortOrder;
sortUrl = getSortUrl(sortKey, sortOrder);
}
this.props.getAccounts(page, size, sortUrl);
}
);
};
componentDidMount() {
this.fetchData(0, 50, undefined);
this.props.getUsersForTenant();
}
toggleEnableColumn = (index) => {
const { columns } = this.state;
var enableOccured = false;
columns[index].enabled = !columns[index].enabled;
columns.forEach((col) => {
if (!enableOccured && col.enabled) {
col.showCheckbox = true;
enableOccured = true;
} else {
col.showCheckbox = false;
}
});
this.setState({
columns: columns,
});
};
render() {
const {
size,
page,
accounts,
columns,
loading,
checkboxActive,
drawerVisible,
} = this.state;
return (
<div className="table-page-container">
<Loading loading={loading} />
<Drawer
title="Add New Account"
placement="right"
closable={true}
visible={drawerVisible}
onClose={this.toggleDrawer}
>
<AddAccount
toggleDrawer={this.toggleDrawer}
assignableUsers={this.props.users}
addAccount={this.addAccount}
/>
</Drawer>
<div className="breadcrumb-container"></div>
<div className="searchbar-container">
<SearchHeader
columns={columns}
checkboxActive={checkboxActive}
fetchData={this.fetchData}
deleteData={this.deleteRows}
toggleDrawer={this.toggleDrawer}
/>
</div>
<Table
size={size}
page={page}
data={accounts}
checkboxActive={checkboxActive}
module={"accounts"}
columns={columns}
toggleEnableColumn={this.toggleEnableColumn}
className="table-container"
selectRow={this.selectRow}
fetchData={this.fetchData}
setSort={this.setSort}
deleteData={this.deleteRows}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
accounts: state.account.accounts,
users: state.user.users,
loading: state.account.loading,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getAccounts: (page, size, sort) => dispatch(getAccounts(page, size, sort)),
addAccount: (accountData, accountType) =>
dispatch(addAccount(accountData, accountType)),
deleteAccounts: (rows, accountType) =>
dispatch(deleteAccounts(rows, accountType)),
getUsersForTenant: () => dispatch(getUsersForTenant()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(AllAccounts);
This is the reducer
import {
GET_ACCOUNT,
GET_ACCOUNTS,
GET_CUSTOMERS,
GET_PROSPECTS,
DELETE_ACCOUNT,
SET_ACCOUNTS_LOADING,
SET_ACCOUNTS_ERROR,
CLEAR_ERRORS,
} from "../actions/types";
const defaultState = {
accounts: [],
customers: [],
prospects: [],
loading: false,
errors: {},
};
const accountReducer = (state = defaultState, action) => {
switch (action.type) {
case GET_ACCOUNTS:
return {
...state,
accounts: action.payload,
errors: {},
};
case GET_CUSTOMERS:
return {
...state,
customers: action.payload,
errors: {},
};
case GET_PROSPECTS:
return {
...state,
prospects: action.payload,
errors: {},
};
case CLEAR_ERRORS:
return {
...state,
errors: {},
};
case SET_ACCOUNTS_LOADING:
return {
...state,
loading: action.payload,
};
case SET_ACCOUNTS_ERROR:
return {
...state,
errors: action.payload,
};
default:
return state;
}
};
export default accountReducer;

What you might need is the static getDerivedStateFromProps(props, state)
This method exists for rare use cases where the state depends on changes in props over time.
In order for your props to flow through your local state, you can write a logic inside that life-cycle method.
static getDerivedStateFromProps(props, state) {
if (!isEqual(props.accounts,state.accounts)) {
return {
accounts: props.accounts
};
}
return null;
}
}
Please take note that I am using isEqual from lodash/isEqual to deep check equality between state and props otherwise it will update everytime props flow in.
Reference: https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops

Related

Breaking down complex Prisma queries in Next.js app

I’ve got a Next.JS app. I want to render a todo feed on the homepage, but also the user page. I'm a bit stuck on how to break down my Prisma queries.
I fetch a big data object using getServerSideProps and pass this to the page component (and using react-query to hydrate and do re-fetching, but not relevant now)
- getRecentTodos (includes todos) for my homepage
- getUserDetailsByName (includes todos) for the user page
export type getRecentTodos = ReturnType<typeof getRecentTodos> extends Promise<
infer T
>
? T
: never
export const getRecentTodos = async (recentItemsAmount = 20) => {
return await prisma.todos.findMany({
where: { done: true },
select: {
id: true,
userId: true,
content: true,
done: true,
createdAt: true,
attachments: true,
todoReplies: {
select: {
id: true,
userId: true,
content: true,
todoReplyLikes: true,
todoId: true,
user: { select: { name: true, displayName: true, image: true } },
},
orderBy: { createdAt: 'asc' },
},
todoLikes: {
select: {
user: true,
},
},
user: {
select: {
name: true,
displayName: true,
image: true,
},
},
},
orderBy: { createdAt: 'desc' },
take: recentItemsAmount,
})
}
export const getUserDetailsByName = async (username: string) => {
return await prisma.user.findUnique({
where: {
name: username,
},
select: {
name: true,
displayName: true,
bio: true,
location: true,
twitter: true,
image: true,
createdAt: true,
todos: {
select: {
id: true,
content: true,
userId: true,
done: true,
updatedAt: true,
createdAt: true,
attachments: true,
user: true,
todoLikes: true,
todoReplies: {
take: 30,
orderBy: { createdAt: 'desc' },
select: {
id: true,
userId: true,
todoId: true,
createdAt: true,
content: true,
user: true,
},
},
},
take: 30,
orderBy: { createdAt: 'desc' },
},
projects: true,
},
})
}
Both queries return ‘todos,’ but they can return it in a slightly different way. The todo feed component expects certain properties to be available
- E.g. displayName on todoReplies
- But on getUserDetailsByName the displayName might not be part of the response or it’s nested one layer deeper or something
How to keep this from getting complex very fast?
You more or less want to select todos in your queries the same way (returning the same and omitting the same, apart of some things like order)
But manually keeping these things in sync over lot’s of queries qet’s complex quickly
Possible solutions?
Should I break the getServerSideProps into multiple fetches?
So instead of one ‘getUserDetailsByName’ which has todos as a relationfield included
fetch user details
fetch todos
This would mean I also have to write more react-query code for refetching etc… because you are dealing with multiple objects. But it does seperate concerns more.
Using Typescript to catch it in my codebase when a function tries to access a property which is not returned from that specific Prisma query? (I’m just now starting to see the possibilities of Typescript for stuff like this)
Should I just standardize the way the todos get created in a prisma query with a function and include that function inside of the Prisma queries? you can include like:
const todoSelect = {
id: true,
userId: true,
content: true,
{.......}
user: {
select: {
name: true,
displayName: true,
image: true,
},
},
}
export type getRecentTodos = ReturnType<typeof getRecentTodos> extends Promise<
infer T
>
? T
: never
export const getRecentTodos = async (recentItemsAmount = 20) => {
return await prisma.todos.findMany({
where: { done: true },
select: todoSelect,
orderBy: { createdAt: 'desc' },
take: recentItemsAmount,
})
}
const userSelect = {
name: true,
{......}
todos: {
select: todoSelect,
take: 30,
orderBy: { createdAt: 'desc' },
},
projects: true,
}
export const getUserDetailsByName = async (username: string) => {
return await prisma.user.findUnique({
where: {
name: username,
},
select: userSelect,
})
}

Define multiple layers in Kepler.gl with different Data?

iam new to react, redux, javascript and also Kepler.gl.
My Question is the following:
I managed it to define multiple datasets without any tutorial, by lucky try. (Kepler.gl has no good documentation sadly)
I tried to define "data2" and fill it with an other source of data, so i can bind it to i specific layer. But every time i associate it to a layer, the layer disappears in the List like there was no data. I checked both sources single and both are fine. Its like it only accept the data const not any other. I also replaced all data const with data2 const and it stopped working. So i think it is a problem to the const. Do you guys see what iam missing?
This is the original code with one data source:
import React from "react";
import keplerGlReducer from "kepler.gl/reducers";
import {createStore, combineReducers, applyMiddleware} from "redux";
import {taskMiddleware} from "react-palm/tasks";
import {Provider, useDispatch} from "react-redux";
import KeplerGl from "kepler.gl";
import {addDataToMap} from "kepler.gl/actions";
import useSwr from "swr";
const reducers = combineReducers({
keplerGl: keplerGlReducer,
});
const store = createStore(reducers, {}, applyMiddleware(taskMiddleware));
const mapboxApiAccessToken = "pk.eyJ1IjoiaHN1bGRlIiwiYSI6ImNsMWNlZ3ZtaTA0MXgzZXF4eGYybmExOWwifQ.-VMvsl27bE0dhoRRY8xMrw";
export default function Kglapp() {
return (
<Provider store={store}>
<Map/>
</Provider>
);
}
function Map() {
const dispatch = useDispatch();
const {data} = useSwr("test", async () => {
const response = await fetch(
".\\mapdatajson\\covid19.json"
);
const data = await response.json();
return data;
})
React.useEffect(() => {
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-1", id: "test-1"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-2", id: "test-2"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-3", id: "test-3"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-4", id: "test-4"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-5", id: "test-5"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-6", id: "test-6"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-7", id: "test-7"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-8", id: "test-8"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-9", id: "test-9"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-10", id: "test-10"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-11", id: "test-11"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-12", id: "test-12"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-13", id: "test-13"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-14", id: "test-14"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-15", id: "test-15"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-16", id: "test-16"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-17", id: "test-17"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-18", id: "test-18"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-19", id: "test-19"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
if (data) {
dispatch(
addDataToMap(
{
datasets: {
info: {label: "test-20", id: "test-20"}, data,
},
option: {
centerMap: true,
readOnly: true,
split: true
},
config: {}
}),
);
}
}
,
[dispatch, data]
);
return (
<KeplerGl
id="test"
mapboxApiAccessToken={mapboxApiAccessToken}
width={window.innerWidth}
height={window.innerHeight}
/>
);
}
Do somebody have any ideas how to set different sources to the layers without let it dissapear for dubious reasons?
Thank you for your help.

Passing information by a form to 3 separate components using MongoDB and React

I am building a website for a comic shop and I am struggling to pass information to 3 different components (back issues, new comics, trades) via a form. Currently I can post data to back issues, however I would like to pass similar information from a form to each of the components depending on whether it is a back issues, new comics or a trade.
I have tried adding product actions for new comics and trades but still don't understand how these can be recognised in a form
What do I need to do so that the user can select on the form one of these options and the data is then correctly posted to the correct component?
My production action for this thus far is:
export function getProductsToShop(skip, limit, filters = []){
const data ={limit, skip, filters}
const request = axios.post(`${PRODUCT_SERVER}/Shop/back_issues`, data)
.then(response => {
return {
size: response.data.size,
articles: response.data.articles
}
});
return {
type: GET_PRODUCTS_TO_SHOP,
payload: request
}
}
export function getProductsToShop(skip, limit, filters = []){
const data ={limit, skip, filters}
const request = axios.post(`${PRODUCT_SERVER}/Shop/new_comics`, data)
.then(response => {
return {
size: response.data.size,
articles: response.data.articles
}
});
return {
type: GET_PRODUCTS_TO_SHOP,
payload: request
}
}
export function getProductsToShop(skip, limit, filters = []){
const data ={limit, skip, filters}
const request = axios.post(`${PRODUCT_SERVER}/Shop/trades`, data)
.then(response => {
return {
size: response.data.size,
articles: response.data.articles
}
});
return {
type: GET_PRODUCTS_TO_SHOP,
payload: request
}
}
My production action for this thus far is:
import React, { Component } from 'react';
import UserLayout from '../../../hoc/user';
import FormField from '../../utils/Form/formfield';
import { update, generateData, isFormValid, populateOptionFields, resetFields } from '../../utils/Form/formActions';
import FileUpload from '../../utils/Form/fileupload';
import {connect} from 'react-redux'
import {getCharacters, getPublishers, addProduct, clearProduct } from '../../../actions/products_actions'
class AddProduct extends Component {
state={
formError:false,
formSuccess:false,
formdata:{
name: {
element: 'input',
value: '',
config:{
label: 'Product title',
name: 'name_input',
type: 'text',
placeholder: 'Enter title'
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
description: {
element: 'textarea',
value: '',
config:{
label: 'Product description',
name: 'description_input',
type: 'text',
placeholder: 'Enter your description'
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
price: {
element: 'input',
value: '',
config:{
label: 'Product price',
name: 'price_input',
type: 'number',
placeholder: 'Enter your price'
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
character: {
element: 'select',
value: '',
config:{
label: 'Product Character',
name: 'character_input',
options:[]
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
issue: {
element: 'input',
value: '',
config:{
label: 'Issue number',
name: 'issue_input',
type: 'number',
placeholder: 'Enter issue number'
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
shipping: {
element: 'select',
value: '',
config:{
label: 'Shipping',
name: 'shipping_input',
options:[
{key:true,value:'Yes'},
{key:false,value:'No'},
]
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
available: {
element: 'select',
value: '',
config:{
label: 'Available, in stock',
name: 'available_input',
options:[
{key:true,value:'Yes'},
{key:false,value:'No'},
]
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
publisher: {
element: 'select',
value: '',
config:{
label: 'Publisher',
name: 'publisher_input',
options:[]
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
publish: {
element: 'select',
value: '',
config:{
label: 'Publish',
name: 'publish_input',
options:[
{key:true,value:'Public'},
{key:false,value:'Hidden'},
]
},
validation:{
required: true
},
valid: false,
touched: false,
validationMessage:'',
showlabel: true
},
images:{
value:[],
validation:{
required: false
},
valid: true,
touched: false,
validationMessage:'',
showlabel: false
}
}
}
updateFields = (newFormData) => {
this.setState({
formdata: newFormData
})
}
updateForm = (element) => {
const newFormdata = update(element,this.state.formdata,'products');
this.setState({
formError: false,
formdata: newFormdata
})
}
resetFieldHandler = () => {
const newFormData = resetFields(this.state.formdata,'products');
this.setState({
formdata: newFormData,
formSuccess:true
});
setTimeout(()=>{
this.setState({
formSuccess: false
},()=>{
this.props.dispatch(clearProduct())
})
},3000)
}
submitForm= (event) =>{
event.preventDefault();
var dataToSubmit = generateData(this.state.formdata,'products');
var formIsValid = isFormValid(this.state.formdata,'products')
if(formIsValid){
this.props.dispatch(addProduct(dataToSubmit)).then(()=>{
if( this.props.products.addProduct.success){
this.resetFieldHandler();
}else{
this.setState({formError: true})
}
})
} else {
this.setState({
formError: true
})
}
}
componentDidMount(){
const formdata = this.state.formdata;
this.props.dispatch(getCharacters()).then( response => {
const newFormData = populateOptionFields(formdata,this.props.products.characters, 'character');
this.updateFields(newFormData)
})
this.props.dispatch(getPublishers()).then( response => {
const newFormData = populateOptionFields(formdata,this.props.products.publishers, 'publisher');
this.updateFields(newFormData)
})
}
imagesHandler = (images) => {
const newFormData = {
...this.state.formdata
}
newFormData['images'].value = images;
newFormData['images'].valid = true;
this.setState({
formdata: newFormData
})
}
render() {
return (
<UserLayout>
<div>
<h1>Add product</h1>
<form onSubmit={(event)=> this.submitForm(event)}>
<FileUpload
imagesHandler={(images)=> this.imagesHandler(images)}
reset={this.state.formSuccess}
/>
<FormField
id={'name'}
formdata={this.state.formdata.name}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'description'}
formdata={this.state.formdata.description}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'price'}
formdata={this.state.formdata.price}
change={(element)=> this.updateForm(element)}
/>
<div className="form_devider"></div>
<FormField
id={'character'}
formdata={this.state.formdata.character}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'issue'}
formdata={this.state.formdata.issue}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'shipping'}
formdata={this.state.formdata.shipping}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'available'}
formdata={this.state.formdata.available}
change={(element)=> this.updateForm(element)}
/>
<div className="form_devider"></div>
<FormField
id={'publisher'}
formdata={this.state.formdata.publisher}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'publish'}
formdata={this.state.formdata.publish}
change={(element)=> this.updateForm(element)}
/>
{this.state.formSuccess ?
<div className="form_success">
Success
</div>
:null}
{this.state.formError ?
<div className="error_label">
Nope
</div>
: null}
<button onClick={(event) => this.submitForm(event)}>
Add product
</button>
</form>
</div>
</UserLayout>
);
}
}
const mapStateToProps = (state) => {
return {
products: state.products
}
}
export default connect(mapStateToProps)(AddProduct);

Is the autocomplete filter doesn't work for columns with numeric values in react-data-grid version 2.0.0?

I am trying to implement a dropdown filter(Singleselect/multiselect/autocpmplete) for a column in my table which contains numeric values for a use case, but in react-data-grid version 2.0.0 and react-data-grid-addons version 2.0.0 I am not able to do this. Is there any work around? and the autocomplete filter only works for columns that have string values.
In the below code the Singleselect filter doesn't work at all and the columns such as ftPad, ptmPad which have numeric value, if I apply autocomplete filter it doesn't work as well, However, it works for string values.
Any workarounds to implement this filter on columns with numeric value?
import React, { Component } from 'react';
import ReactDataGrid from "react-data-grid";
import { Toolbar, Data, Filters} from "react-data-grid-addons";
const Selectors = Data.Selectors;
const AutoCompleteFilter = Filters.AutoCompleteFilter;
const NumericFilter = Filters.NumericFilter;
const SingleSelectFilter = Filters.SingleSelectFilter;
export default class PadMonitoringReport extends Component {
constructor(props) {
super(props);
this._columns = [
{
key: 'nodeId',
name: 'WarehouseId',
sortable: true,
filterable: true,
filterRenderer: SingleSelectFilter
},
{
key: 'nodeType',
name: 'Node Type',
filterable: true,
sortable: true,
resizable: true,
filterRenderer: AutoCompleteFilter
},
{
key: 'backlog',
name: 'Backlog',
sortable: true,
filterable: true,
filterRenderer: NumericFilter
},
{
key: 'ptmPad',
name: 'PTM Pad',
filterable: true,
sortable: true,
filterRenderer: NumericFilter
},
{
key: 'ftPad',
name: 'FT Pad',
filterable: true,
sortable: true,
filterRenderer: NumericFilter
},
{
key: 'maxCPTRisk',
name: 'Max CPT Risk',
sortable: true,
filterable: true,
filterRenderer: NumericFilter
},
{
key: 'pickToManifestTimeInSeconds',
name: 'Pick to manifest (time in sec)',
sortable: true,
filterable:true,
filterRenderer: NumericFilter
}
];
this.state = {
rows: props.data,
filters: {},
sortColumn: null,
sortDirection: null
};
this.rowGetter = this.rowGetter.bind(this);
this.handleGridSort = this.handleGridSort.bind(this);
this.handleFilterChange = this.handleFilterChange.bind(this);
this.onClearFilters = this.onClearFilters.bind(this);
this.getRows = this.getRows.bind(this);
this.getSize = this.getSize.bind(this);
this.getValidFilterValues = this.getValidFilterValues.bind(this);
}
getRows() {
return Selectors.getRows(this.state);
}
getSize() {
return this.getRows().length;
}
rowGetter(i) {
//console.log("it is............")
//console.log(this.state.rows[i])
let rows = this.getRows();
return rows[i];
}
handleGridSort(sortColumn, sortDirection) {
this.setState({ sortColumn: sortColumn, sortDirection: sortDirection });
}
handleFilterChange(filter) {
let newFilters = Object.assign({}, this.state.filters);
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
this.setState({ filters: newFilters });
}
onClearFilters() {
// all filters removed
this.setState({filters: {} });
}
getValidFilterValues(columnId) {
let values = this.state.rows.map(r => r[columnId]);
return values.filter((item, i, a) => { return i === a.indexOf(item); });
}
render() {
return (
<div>
<ReactDataGrid
onGridSort={this.handleGridSort}
enableCellSelect={true}
columns={this._columns}
rowGetter={this.rowGetter}
rowsCount={this.getSize()}
toolbar={<Toolbar enableFilter={true}/>}
onAddFilter={this.handleFilterChange}
getValidFilterValues={this.getValidFilterValues}
onClearFilters={this.onClearFilters}
/>
</div>
);
}
}
I don't know is it relevant now or not, I faced this issue and solved it.
You need to convert your number array to string array for getValidFilterValues props
For instance:
getValidFilterValues={this.getValidFilterValues}
getValidFilterValues = (columnId) => {
let values = this.state.rows.map(r => r[columnId]);
var r = values.filter((item, i, a) => {
return i === a.indexOf(item);
});
return r.map(v => v.toString());
};

Looping nested array Object in ListView React native

I currently trying to play around with react-native..
I want to make a list view that get the dataSource from var which contain an object that have nested array inside.
var questionArray = [
{
question: "What is the emotion of the user? choose all that apply!",
answerArray: [
{ name: "Cheerful", status: true, answered: false },
{ name: "Impulsive", status: false, answered: false },
{ name: "Firm", status: false, answered: false },
{ name: "Merry", status: true, answered: false },
{ name: "Jumpy", status: false, answered: false },
{ name: "Energetic", status: false, answered: false },
{ name: "Glowing", status: false, answered: false },
{ name: "Animated", status: false, answered: false },
{ name: "Lively", status: false, answered: false },
{ name: "Creepy", status: false, answered: false },
{ name: "Excited", status: true, answered: false },
{ name: "Tense", status: false, answered: false },
{ name: "Unrestful", status: false, answered: false },
{ name: "Pleased", status: true, answered: false },
{ name: "Unrestful", status: false, answered: false },
{ name: "Hasty", status: false, answered: false },
{ name: "Delighted", status: true, answered: false },
{ name: "Hedonistic", status: false, answered: false },
{ name: "Eager", status: false, answered: false },
{ name: "Joyful", status: false, answered: false },
]
},
{
question: "What is the best way to describe this person's character? Choose all that apply.",
answerArray: [
{ name: "Cheerful", status: true, answered: false },
{ name: "Impulsive", status: false, answered: false },
{ name: "Firm", status: false, answered: false },
{ name: "Merry", status: true, answered: false },
{ name: "Jumpy", status: false, answered: false },
{ name: "Energetic", status: false, answered: false },
{ name: "Glowing", status: false, answered: false },
{ name: "Animated", status: false, answered: false },
{ name: "Lively", status: false, answered: false },
{ name: "Creepy", status: false, answered: false },
{ name: "Excited", status: true, answered: false },
{ name: "Tense", status: false, answered: false },
{ name: "Unrestful", status: false, answered: false },
{ name: "Pleased", status: true, answered: false },
{ name: "Unrestful", status: false, answered: false },
{ name: "Hasty", status: false, answered: false },
{ name: "Delighted", status: true, answered: false },
{ name: "Hedonistic", status: false, answered: false },
{ name: "Eager", status: false, answered: false },
{ name: "Joyful", status: false, answered: false },
]
}
];
and I called this variable inside render ListView which trigger renderRow function..
_renderRow( rowData, sectionID, rowID ) {
return (
<View>
<Text>{rowData.question}</Text>
<Text>{rowData.answerArray[0].name}</Text>
</View>
);
}
render() {
return (
<View>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
/>
</View>
);
}
How do I render the loop inside this listView ?
Even the when I hard coded the index like above code, it's not working..
Thank you.
You should make two separate Components: Question & Answer
I've made a Codepen for you as an example, using your data
At first, I pass the questionsArray as prop to the ListView Component:
class Application extends React.Component {
render() {
return (
<div>
{ questionsArray.map((question, key) => <ListView key={`listview-${key}`} question={question} />) }
</div>
);
}
}
Then, you can pass the answers to the ListViewItem Component, like this:
class ListView extends React.Component {
render() {
return (
<div>
{this.props.question.question}
{this.props.question.answerArray.map((answer, key) => <ListViewItem answer={answer} />)}
</div>
);
}
}
This is the ListViewItem:
class ListViewItem extends React.Component {
render() {
return (
<div>
{this.props.answer.name}
</div>
);
}
}

Resources