In my React code, I have an Employees page rendering both a Table ponent and a Filter ponent. The Filter code manipulates some data stored in the Employees page and then updates the Filter's state through useEffect. The data in question is passed as a prop to both the Filter ponent and the Table ponent. However, only the Filter ponent is re-rendering when this change happens. Excerpts of code below. Sample of filter function:
function filterFunction() {
let spliceArray = [];
if ($('#firstNameFilter').val() !== '') {
for (let i = 0; i < props.employees.length; i++) {
if(props.employees[i].firstName !== $('#firstNameFilter').val()) {
spliceArray.push(i);
}
}
for (let i = spliceArray.length - 1; i >= 0; i--) {
props.employees.splice(spliceArray[i], 1);
}
}
setUseFilter(true);
}
Sample of employees array on main page:
let employees = [
{
_id: 2,
firstName: 'Some',
lastName: 'One',
department: 'Somewhere',
jobTitle: 'Something',
email: '[email protected]',
phoneExtension: 67890
},
]
Sample of Table rendering code:
<tbody>
{props.employees.map((employee) => {
<tr>
<td><i id={employee._id + "upd"} className="fas fa-edit" onClick={handleEdit}></i></td>
<td><i id={employee._id + "del"} className="fas fa-trash-alt" onClick={deleteEmployee}></i></td>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.department}</td>
<td>{employee.jobTitle}</td>
<td>{employee.email}</td>
<td>{employee.phoneExtension}</td>
</tr>
</tbody>
So ideally, the filter would update the employees array and then the table would re-render with the updated array to reflect only the appropriate parts of the array per the filter. However, currently the array is being updated but the table is not re-rendering.
I am relatively new to React but my understanding was that a ponent would re-render whenever its state or props changed. Although I am unaware of a way to change the state of the Table ponent from within the Filter ponent, it seems like the change in the employees array prop should trigger it.
Any help is appreciated.
Per request, full Employee file below:
import React from 'react';
import Title from '../ponents/Title/Title.js';
import Table from '../ponents/Table/Table.js';
import Filter from '../ponents/Filter/Filter.js';
import { Container, Row, Col } from '../ponents/Grid/Grid.js';
import axios from 'axios';
let employees = [
{
_id: 2,
firstName: 'Some',
lastName: 'One',
department: 'Somewhere',
jobTitle: 'Something',
email: '[email protected]',
phoneExtension: 67890
},
{
_id: 3,
firstName: 'Test',
lastName: 'McTester',
department: 'Sales',
jobTitle: 'Seller',
email: '[email protected]',
phoneExtension: 13579
}];
let employeesControl = [
{
_id: 2,
firstName: 'Some',
lastName: 'One',
department: 'Somewhere',
jobTitle: 'Something',
email: '[email protected]',
phoneExtension: 67890
},
{
_id: 3,
firstName: 'Test',
lastName: 'McTester',
department: 'Sales',
jobTitle: 'Seller',
email: '[email protected]',
phoneExtension: 13579
}];
function Employees() {
return(
<Container fluid>
<Row>
<Col size="md-12">
<Title>Employee Directory</Title>
</Col>
</Row>
<Row>
<Col size="md-12">
<Filter employees={employees} employeesControl={employeesControl}/>
</Col>
</Row>
<Row>
<Col size="md-12">
<Table employees={employees} />
</Col>
</Row>
</Container>
);
}
export default Employees;
In my React code, I have an Employees page rendering both a Table ponent and a Filter ponent. The Filter code manipulates some data stored in the Employees page and then updates the Filter's state through useEffect. The data in question is passed as a prop to both the Filter ponent and the Table ponent. However, only the Filter ponent is re-rendering when this change happens. Excerpts of code below. Sample of filter function:
function filterFunction() {
let spliceArray = [];
if ($('#firstNameFilter').val() !== '') {
for (let i = 0; i < props.employees.length; i++) {
if(props.employees[i].firstName !== $('#firstNameFilter').val()) {
spliceArray.push(i);
}
}
for (let i = spliceArray.length - 1; i >= 0; i--) {
props.employees.splice(spliceArray[i], 1);
}
}
setUseFilter(true);
}
Sample of employees array on main page:
let employees = [
{
_id: 2,
firstName: 'Some',
lastName: 'One',
department: 'Somewhere',
jobTitle: 'Something',
email: '[email protected]',
phoneExtension: 67890
},
]
Sample of Table rendering code:
<tbody>
{props.employees.map((employee) => {
<tr>
<td><i id={employee._id + "upd"} className="fas fa-edit" onClick={handleEdit}></i></td>
<td><i id={employee._id + "del"} className="fas fa-trash-alt" onClick={deleteEmployee}></i></td>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.department}</td>
<td>{employee.jobTitle}</td>
<td>{employee.email}</td>
<td>{employee.phoneExtension}</td>
</tr>
</tbody>
So ideally, the filter would update the employees array and then the table would re-render with the updated array to reflect only the appropriate parts of the array per the filter. However, currently the array is being updated but the table is not re-rendering.
I am relatively new to React but my understanding was that a ponent would re-render whenever its state or props changed. Although I am unaware of a way to change the state of the Table ponent from within the Filter ponent, it seems like the change in the employees array prop should trigger it.
Any help is appreciated.
Per request, full Employee file below:
import React from 'react';
import Title from '../ponents/Title/Title.js';
import Table from '../ponents/Table/Table.js';
import Filter from '../ponents/Filter/Filter.js';
import { Container, Row, Col } from '../ponents/Grid/Grid.js';
import axios from 'axios';
let employees = [
{
_id: 2,
firstName: 'Some',
lastName: 'One',
department: 'Somewhere',
jobTitle: 'Something',
email: '[email protected]',
phoneExtension: 67890
},
{
_id: 3,
firstName: 'Test',
lastName: 'McTester',
department: 'Sales',
jobTitle: 'Seller',
email: '[email protected]',
phoneExtension: 13579
}];
let employeesControl = [
{
_id: 2,
firstName: 'Some',
lastName: 'One',
department: 'Somewhere',
jobTitle: 'Something',
email: '[email protected]',
phoneExtension: 67890
},
{
_id: 3,
firstName: 'Test',
lastName: 'McTester',
department: 'Sales',
jobTitle: 'Seller',
email: '[email protected]',
phoneExtension: 13579
}];
function Employees() {
return(
<Container fluid>
<Row>
<Col size="md-12">
<Title>Employee Directory</Title>
</Col>
</Row>
<Row>
<Col size="md-12">
<Filter employees={employees} employeesControl={employeesControl}/>
</Col>
</Row>
<Row>
<Col size="md-12">
<Table employees={employees} />
</Col>
</Row>
</Container>
);
}
export default Employees;
Share
Improve this question
edited Apr 26, 2020 at 0:12
mjbradford7
asked Apr 25, 2020 at 23:54
mjbradford7mjbradford7
11 gold badge1 silver badge2 bronze badges
1
-
please add the parent of both
Filter
andTable
ponent code, probably the issue there – Hagai Harari Commented Apr 26, 2020 at 0:08
5 Answers
Reset to default 6If I understand well, you have and as children of your ponent.
There is two solutions for your problem :
- Manipulate your data in your parent before to pass it down to your child.
- Implement a context API (remanded) to handle a "global context" though your app. This way you will be able to manipulate your data in your ponent and dispatch the result to update your store (Consider the store as the content of your context). If your ponent get the state through useContext() hook and pass it as props in your children, then updating the store automatically re-render them. The context Api is pretty easy to understand, you'll just have to learn about the other hooks useReducer() and and useContext().
For all of this knowledge required, I can only give you the best article I have ever found on this subject : Mixing Hooks and Context API.
The official doc is quite understandable as well and provide clear example of what you need.
- Context API
- Hooks
Hope this help !
Your main issue is that you are mutating props. In React, you can't mutate props or state. The better way of working with this is to pass a callback function back to the Filter ponent that calls setState (or the hook equivalent) in the Employees ponent.
In addition to that, you'll need to make your employees
and employeesControl
part of the state in Employees ponent.
The reason for this is because React uses references to determine if values changed and the ponents need to re-render. It also uses the setState functionalities to tell the ponents they need to re-render. Then they'll re-render the children as well.
If you need data passed into ponents that are further away than parent-child and improved data flow, then you can use Context, Redux, or one of the myriad other state management functionalities in the React ecosystem.
In react.js when state or props changed, the ponent re-rendered. You can mutate state, but you shouldn't mutate props. In react.js props are read-only.
In addition to Quentin Grisel's answer, there's a 3rd way, i.e. to forwardRef
a force-rerender function from the ponent to the one who controls it.
const MyComponent = forwardRef((props, ref) => {
const rerender = useRerender();
useEffect(() => {
ref.current = rerender
}, []);
//...
})
An implementation of useRerender
hook can be found here.
I was facing this problem today as well and found a new, realy easy, but kindof ugly solution to it.
Basically I just used navigation.goBack() and navigation.push('yourPage') in sequence.
It shows the previous page for a bit, but works pletely fine otherwise.