Getting an Empty array in props - reactjs

I am trying to make table using react-table in which one of the columns will be a toggle-switch in typescript. I am using the following code to create a react-switch.
const tableInstance = useTable({
columns,
data},(hooks)=> {
console.log("setting buildings: ",data);
hooks.visibleColumns.push((columns) => columns.map((column)=>column.id === "cevacrunningstatus"?{...column,Cell:({row})=>
{return <BuildingStartStopSwitch row_={row} data__={buildings} setdata={setbuildings}/>}}:column));
}
)
I am using the following React function component
interface props{
row_:Row.Row<any>;
data__:BuildingControlStatus[];
setdata: React.Dispatch<React.SetStateAction<BuildingControlStatus[]>>;
}
const BuildingStartStopSwitch:React.FC<props> = ({row_,data__,setdata}) => {
const [state,setState] = useState<boolean>(row_.values.runningstatus);
const handleChange = (checked:boolean) => {
setState(checked);
console.log("Data before statechange: ",data__)
setdata(data__.map((data_)=>row_.values.ccid === data_.ccid?({...data_,runningstatus:checked}):data_))
}
console.log("Data after statechange: ",data__)
return (
<Switch onChange={handleChange} checked={state}/>
);
};
export default BuildingStartStopSwitch;
I have the following issue:
The array data__ is turning up as an empty array inside BuildingStartStopSwitch. The data variable which is assigned to data__ contains items, but the same is not reflected inside BuildingStartStopSwitch. I am trying to update the data variable(which is the table data) to reflect the status of toggle switch.
I have an input cell against each toggle switch in a row. When the switch is checked, the input should be enabled and when the switch is unchecked , it should be disabled. I am not sure how to implement.
Thanks in advance!

Related

react ant design table with json - updating data without key problem

I would like to display some json data (coming from my backend and handled in a hook) in a similar way to this : https://ant.design/components/table/#components-table-demo-tree-data
I have a column with a checkbox and a column with an input (both must be editable by the user) and the final json state must be updated with the new datas.
Here you can find the structure of my json : https://pastebin.com/wA0GCs1K
Here you have a screen of the final result :
The code I used to fetch the data :
const [dataServiceInterface, setDataServiceInterface] = useState(null);
useEffect(() => {
CreateApiService.fetchDataServiceInterface(questionFiles, responseFiles).then((result) => {
if (result != null) {
setDataServiceInterface(result);
}
});
}, []);
Here you have the code I used to update the attributes (column constant for the second part) :
const onInputChange = (key, record, index) => (e) => {
e.preventDefault();
console.log(key);
console.log(index);
console.log(record);
console.log(e.target.value);
dataServiceInterface.itemsQ[index][key] = e.target.value;
};
// some other code here
{
title: "Json Name",
dataIndex: "name",
key: "name",
render: (text, record, index) => (
<Input
//defaultValue={text}
value={text}
onChange={onInputChange("name", record, index)}
/>
),
},
Problem is (I think) : As I dont have a key defined in my json datas (parents and children), when I try to update the children it dosent work. I can't change the structure of the json because it's a business constraint. I was thinking of copying my json data in another state, then add the keys ... and still didn't try this solution and don't know if it works. I will update this if it's the case.
Meanwhile, if someone had the same issue and has any idea/hint/suggestion, would appreciate very much. Thx.
I think the problem here is that the component is not rendering the data once you have it. This can be solved by using useState hook, you should lookup for the docs.
I would love to get a little bit more of the component that you are building. But the approach to this would be something like this:
const Component = () {
const [data, useData] = useState([]);
const onInputChange = (key, record, index) => (e) => {
e.preventDefault();
const data = // You get fetch the data here
useData(data);
};
return <>
{
data && data.itemsQ.map((item) => {
// here you display the information contained in each item
})
}
</>
}

React.js - Using one component multiple times with different settings

I'm trying to create a re-usable component that wraps similar functionality where the only things that change are a Title string and the data that is used to populate a Kendo ComboBox.
So, I have a navigation menu that loads (at the moment) six different filters:
{
context && context.Filters && context.Filters.map((item) => getComponent(item))
}
GetComponent gets the ID of the filter, gets the definition of the filter from the context, and creates a drop down component passing in properties:
function getComponent(item) {
var filterDefinition = context.Filters.find(filter => filter.Id === item.Id);
switch (item.DisplayType) {
case 'ComboBox':
return <DropDownFilterable key={item.Id} Id={item.Id} Definition={filterDefinition} />
default:
return null;
}
}
The DropDownFilterable component calls a service to get the data for the combo box and then loads everything up:
const DropDownFilterable = (props) => {
const appService = Service();
filterDefinition = props.Definition;
console.log(filterDefinition.Name + " - " + filterDefinition.Id);
React.useEffect(() => {
console.log("useEffect: " + filterDefinition.Name + " - " + filterDefinition.Id);
appService.getFilterValues(filterDefinition.Id).then(response => {
filterData = response;
})
}, []);
return (
<div>
<div className="row" title={filterDefinition.DisplayName}>{filterDefinition.DisplayName}</div>
<ComboBox
id={"filterComboBox_" + filterDefinition.Id}
data={filterData}
//onOpen={console.log("test")}
style={{zIndex: 999999}}
dataItemKey={filterDefinition && filterDefinition.Definition && filterDefinition.Definition.DataHeaders[0]}
textField={filterDefinition && filterDefinition.Definition && filterDefinition.Definition.DataHeaders[1]}
/>
</div>
)
}
Service call:
function getFilterValues(id) {
switch(id) {
case "E903B2D2-55DE-4FA3-986A-8A038751C5CD":
return fetch(Settings.url_getCurrencies).then(toJson);
default:
return fetch(Settings.url_getRevenueScenarios).then(toJson);
}
};
What's happening is, the title (DisplayName) for each filter is correctly rendered onto the navigation menu, but the data for all six filters is the data for whichever filter is passed in last. I'm new to React and I'm not 100% comfortable with the hooks yet, so I'm probably doing something in the wrong order or not doing something in the right hook. I've created a slimmed-down version of the app:
https://codesandbox.io/s/spotlight-react-full-forked-r25ns
Any help would be appreciated. Thanks!
It's because you are using filterData incorrectly - you defined it outside of the DropDownFilterable component which means it will be shared. Instead, set the value in component state (I've shortened the code to include just my changes):
const DropDownFilterable = (props) => {
// ...
// store filterData in component state
const [filterData, setFilterData] = React.useState(null);
React.useEffect(() => {
// ...
appService.getFilterValues(filterDefinition.Id).then(response => {
// update filterData with response from appService
setFilterData(response);
})
}, []);
// only show a ComboBox if filterData is defined
return filterData && (
// ...
)
}
Alternatively you can use an empty array as the default state...
const [filterData, setFilterData] = React.useState([]);
...since the ComboBox component accepts an array for the data prop. That way you won't have to conditionally render.
Update
For filterDefinition you also need to make sure it is set properly:
const [filterDefinition, setFilterDefinition] = React.useState(props.Definition);
// ...
React.useEffect(() => {
setFilterDefinition(props.Definition);
}, [props.Definition]);
It may also be easier to keep filterDefinition out of state if you don't expect it to change:
const filterDefinition = props.Definition || {};

How to lock a row in a react material-table (with remote data) on edit?

Using material-table i try to lock a row on the server
using a remote api call with a value from the edited row, like an id column, as soon as the user clicks the edit action button on that row.
The onRowUpdate() callback is called when the data is to be written, so too late to be useful here.
What can be used to achieve this pre-Edit callback?
Overriding EditRow somehow...?
Not yet perfect but seems the way to go:
add a new state
const [lock, setLock] = useState({});
and
components={{
EditRow: (props) => <MyTableEditRow onEdit={applyLock} offEdit={removeLock} {...props} />,
}}
to the tabel definition and a new Component MyTableEditRow
const MyTableEditRow = (props) => {
const { onEdit, offEdit, ...rest } = props;
useEffect(() => {
console.log('MyTableEditRow useEffect');
if (onEdit && typeof onEdit === 'function') onEdit(props.data);
return () => {
console.log('MyTableEditRow cleanup');
if (offEdit && typeof offEdit === 'function') offEdit(props.data);
};
}, [onEdit, offEdit, props.data]);
return <MTableEditRow {...rest} />;
};
this components is a small wrapper around the built in edit row component and calls the two given function on create and destroy respectively who should get and remove the lock.
with the two callback to do the lock stuff on the rmeote api like: (add some error handling and stuff, getLock() and deleteLock() are basically axios calls to the remote api...)
const applyLock = (rowData) => {
getLock(api, rowData._id).then((lock_data) => {setLock(lock_data);})
}
};
const removeLock = () => {
deleteLock(api, lock).then((r) => { setLock({}); });
}
};
TODO
What is not working right now is onRowDelete(). the cancel button works, but the save just does nothing. Adding a row works, strangely...
EDIT
onRowDelete works, bug in my code.(do not use a non-existing state...)

How to update same redux state on two different components simultaneously?

I have a React Native scenario in which I have two components, one is a textInput and the other is a SVG drawing. By filling out the textInput and pressing a button, the text is inserted as a data instance to an array stored in a redux state using a dispatch action (let's call it textListState).
//TEXT INPUT COMPONENT
//this code is just for presentation. it will cause errors obviously
const dispatch = useDispatch();
const submitHandler = (enteredText) => {
dispatch(submitData(enteredText))
};
return(
<TextInput ...... />
<TouchableOpacity onPress={() => submitHandler(enteredText) } />
)
Now, the SVG component updates all the features' fill color stored in the textListState using a function (e.g. setFillColor). This is done inside a useEffect hook with a dependency set to a prop (e.g. propA).
Now the problem is that I need to add textListState as a dependency to the useEffect because I want the newly entered text from the textInput to be included in the SVG component. But by doing so, I am creating an infinite loop, because the setFillColor also updates the textListState.
//SVG COMPONENT
const [textList, setTextList] = useState([]);
const textListState = useSelector(state => state.textListState);
const dispatch = useDispatch();
const setFillColor= useCallback((id, fill) => {
dispatch(updateFillColor(id, fill))
}, [dispatch]);
useEffect(() => {
//INFINITE LOOP BECAUSE textListState KEEPS UPDATING WITH setFillColor
const curTextList = [];
for (const [i, v] of textListState.entries()) {
setFillColor(5, "white")
curTextList.push(
<someSVGComponent />
)
}
setTextList(curTextList);
}, [props.propA, textListState])
return(
<G>
{textList.map(x => x)}
</G>
)
How can I achieve to be able to add the newly inserted text to the SVG component without creating an infinite loop?
EDIT:
The redux action
export const UPDATE_TEXT = "UPDATE_TEXT";
export const SUBMIT_DATA = "SUBMIT_DATA";
export const updateFillColor = (id, fill) => {
return { type: UPDATE_TEXT, id: id, fill: fill }
};
export const submitData= (text) => {
return { type: SUBMIT_DATA, text: text }
};
The redux reducer
import { TEXT } from "../../data/texts";
import { UPDATE_TEXT } from "../actions/userActions";
import { INSERT_TEXT } from "../actions/userActions";
// Initial state when app launches
const initState = {
textListState: TEXT
};
const textReducer = (state = initState, action) => {
switch (action.type) {
case INSERT_TEXT :
return {
...state,
textListState: [
...state.textListState,
action.text
]
}
case UPDATE_TEXT :
const curText = state.textListState.filter(text => text.id === action.id)[0];
return {
...state,
textListState: [
...state.textListState.slice(0, curIndex), //everything before current text
curText.fill = action.fill,
...state.textListState.slice(curIndex + 1), //everything after current text
]
}
default:
return state;
}
};
export default textReducer;
if you want to trigger useEffect only when new text is added then use textListState.length instead of textListState in dependency list.
If you want to do it both on update and insert (i.e. you want to fire even when text is updated in input) then use a boolean flag textUpdated in textListState and keep toggling it whenever there is a update or insert and use textListState.textUpdated in dependency list.

reactjs and redux: How to implement filter text box for a rows of data

I am learning reactj and redux. Somehow i managed to get data from api and display them in table. The table is shown below.
I want to add filter text box below each column header so that when i type text, it shows those values matching the results.
What is the flow using redux. Or any example
I just typed this up, its just for a general idea of how to filter data in your mapstate function. This is called computing derived data https://redux.js.org/docs/recipes/ComputingDerivedData.html and its good to use tools like Reselect for performance benefits. This isnt working code, but its just to give you an idea of how it is usually done so you dont get duplicate state in your reducers.
class Table extends React.Component {
filterData = (event, column) => {
this.props.someFilterAction(event.target.value, column)
}
renderItem = (item) => {
return <someItemDiv searchAction={(e) => this.filterData(e, item.column) } />
}
render(){
<div>
{ this.props.data.map(this.renderItem) }
</div>
}
}
function mapStateToProps(state) {
const searchItem = state.searchData;
// reselect is good for stuff like this
const filteredData = state.tableData.filter(item => item[searchItem.column].text.indexOf(searchItem.input) !== -1 )
// or some kind of search criteria you want
return {
data: filteredData,
searchItem: state.searchData
}
}
const mapDispatchToProps = {
someFilterAction: YourReduxActionCreator
}
export default connect(mapStateToProps, mapDispatchToProps)(Table);
function YourReduxActionCreator(input, column) {
return {
type: FILTER_DATA,
payload: {
column,
input,
}
}
}
function yourReducer() {
switch(state, action) {
case FILTER_DATA:
return action.payload
default:
return state;
}
}
Hey you just need to create 2 selectors.
The first one : getTableData() that get your whole table data from your store
Then getFilteredTableData() which will filter the result of getTableData() using the form values of your field (your keyword and the column).
The selector getFilteredTableData() should be the source of data for your table.
So as soon as your form change your table will be refiltered !

Resources