最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to re-render one component from another in React - Stack Overflow

programmeradmin0浏览0评论

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 and Table ponent code, probably the issue there – Hagai Harari Commented Apr 26, 2020 at 0:08
Add a ment  | 

5 Answers 5

Reset to default 6

If I understand well, you have and as children of your ponent.

There is two solutions for your problem :

  1. Manipulate your data in your parent before to pass it down to your child.
  2. 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.

发布评论

评论列表(0)

  1. 暂无评论