Goal
I'm currently attempting to use React-Transition-Group to animate the rows of a table in and out. There are two different scenarios that can happen:
the entire contents of the table will be swapped out (most common option)
individual rows may be added and removed
Where I am now
I used this todo list example as a starting point to make the table animation. Here is my sandbox.
My main problem is when the table data is being completely swapped out, you can see both data sets at the same time. The old data is sliding out while the new data is sliding in. This causes the old data to snap down as it's transitioning out (see image below).
Ideally the initial table data would completely disappear before the new data comes in, but instead the new data pops in while the old data is there.
Code
Table Prop that maps through rows
<table>
<TransitionGroup component="tbody">
{this.props.tables[this.props.currentTable].rows.map((row, i) => {
return (
<CSSTransition
timeout={500}
classNames="SlideIn"
key={`${row}-${i}`}
>
<tr>
{row.map((column, i) => {
return <td>{column}</td>;
})}
</tr>
</CSSTransition>
);
})}
</TransitionGroup>
</table>
Function to change table dataset:
changeTable = () => {
this.setState({
currentTable:
this.state.currentTable < this.state.tables.length - 1
? this.state.currentTable + 1
: 0
});
};
Related
I am using react-virtualized to create an infinite scroll in my React app. Because list items can be of different heights, I am using CellMeasurer from react-virtualized to render list items. I also have an intersection observer that listens for scroll and loads more data as user scrolls down. The code looks like this:
const rowRenderer = ({ key, index, style, parent }) =>
<CellMeasurer
cache={cache.current}
parent={parent}
columnIndex={0}
rowIndex={index}>
<ListItem
key={key}
listData={listData[index]}
style={style}
setObserver={setObserver}
/>
</CellMeasurer>
<AutoSizer>
{
() => ({ width, height }) =>
<List
width={width}
height={height}
rowHeight={cache.current.rowHeight}
deferredMeasurementCache={cache.current}
rowCount={listData.length}
rowRenderer={rowRenderer}
/>
}
</AutoSizer>
This works fine except React gives a warning that each item of List should have a unique key. If I move the key={key} to CellMeasurer from ListItem to fix this, the warning goes away but the code doesn't load new data anymore on scroll.
I am not sure why this is happening?
The app also allows deleting a list item from the list. However, when the deletion happens, the list scrolls down by a significant number of rows, rather than staying at that scroll position. How do I fix this?
ReactJS >
I want to show by default 10 rows And a button with "Add a new question" so that would be the 11th row.
current row image here
Currently it's only one row [refer to the image below]. And I want 10 initial rows and then add as required. And as many rows can add it could be able to scroll. And on submit I would have all the filled data from that input component. Maybe just console.log when to submit or to alert for now.
NOTE- using formik (could also use state)
Not adding code, because it's a large component. perhaps here's a codesandbox, When I am looking for a solution. If anyone can help me would be great. Please got stuck with this.
I don't know the use case in your case. But here is the simple way to do that.
export default function App() {
const [elements, setElements ] = useState([<QuestionColumn />]);
const element = () => (
<QuestionColumn />
)
console.log(elements)
return (
<div className="App">
{elements.map((child) => child)}
<button
onClick={() => {
setElements([
...elements,
element()
])
}}
>
Add New Question
</button>
</div>
);
}
I was trying to develop a dynamic list component, which involves state snyc. I have to admit the current design is terrible, it's not readable but at least I've made some progress. However I can't solve an issue, the delete has a bug.
https://codesandbox.io/s/wkqoz0wwv8
To reproduce it:
click 'add more' twice, to add 2 new item.
fill in the value for the last dropdown.
delete the 2nd list.
Something is not right there, it supposes to delete the 2nd item and kep the state of the last item.
The problem is not how you are removing the state but how you are rendering it, you are actually using the times function to creates a loop will will iterate row_count number of times and give you the index,
All you need to do is iterate over your data object like
return (
<div className="companyContactInfo-addContact">
{Object.keys(company_contacts).map(i => this.singleContact(i))}
<div className="row">
<div className="col-xs-12">
<a onClick={::this.addNewRow}>
<span>Add</span>
</a>
</div>
</div>
</div>
);
Also you once the duplicated variable is modified you could just assign it to state rather than using spread operator
handleInputChange(e, index) {
let targetedContact = this.state.company_contacts[index];
targetedContact.value = e.target.value;
this.setState({
company_contacts: {
...this.state.company_contacts,
[index]: targetedContact
}
});
}
Working Sandbox
I'm developing a contacts application where all data is stored on client-side temporarily. I've used material-ui table to display the contacts.
When add new contact button on the bottom right is clicked it displays a Dialog with a form in it. When save button is clicked the data is saved in the state. But the problem I'm facing is how to insert a new row within the table with the form data in it. I searched for it but I couldn't find even a single query regarding the same. The add new contact button is in AddContact component and table is in Contacts component but both have same parent component which is in Main. So in a nutshell I'm not able to display the new contact in the table.
Maintain an array in state variable, when you click on submit in Dialog, push that data in state array. Use the array to generate the rows dynamically, like this:
<Table>
<TableHeader>
<TableRow>
/*Define the headers*/
</TableRow>
</TableHeader>
<TableBody>
{this._generateRows()}
</TableBody>
</Table>
Write the _generateRows function like this to generate the rows:
_generateRows(){
return this.state.data.map(el => {
return <TableRow>
<TableRowColumn>{el.a}</TableRowColumn>
<TableRowColumn>{el.b}</TableRowColumn>
<TableRowColumn>{el.c}</TableRowColumn>
</TableRow>
})
}
Reference : Material-UI table.
Note: Replace el.a, el.b, el.c with the original data.
I see you are using react. This means that when you bind a list of items to the
row components in your render function.
{list.map(item => <RowComponent key={item.id}/>)}
Now when you click the button all you have to do is push to this list and your new row is rendered on screen.
I'm currently trying to highlight the top element in a react native ListView.
Is it possible to for ListView identify which row component is located at the top of the view on scroll?
If not, how would I go about selecting the lowest integer rowID of the visible rows?
renderRow(rowData, sectionID, rowID) {
return (
<MyRow
{...rowData}
key={rowData.detailID}
onDetailPress={() => this.onDetailPress(rowData, rowID)}
/>
);
}
You can use onChangeVisibleRows which gives you a list of the visible rows and the rows that have changed their visibility. Something like this should do:
<MyRow
{...rowData}
key={rowData.detailID}
onDetailPress={() => this.onDetailPress(rowData, rowID)}
onChangeVisibleRows={(visible, changed) => this.highlightRow(visible[0])}
/>
You would probably need to update the data source with the item you want to be highlighted containing a state that will indicate it.