Passing a function with React Context API to child component nested deep in the tree

31,503

Maybe something like that?

<Context.Provider
    value={{
        state: this.state,
        onSortClient: this.onSortClient,
    }}
>
    {this.props.children}
</Context.Provider>

So, value.state will be your state, value.onSortClient will be your function.

Share:
31,503
acd37
Author by

acd37

Updated on July 09, 2022

Comments

  • acd37
    acd37 almost 2 years

    I'm using React Context API for the first time. I have a table that generates a list of clients. Originally, I stored the clients in an array in state, and in the same page I had a function that sorted the clients based on click.

    I have moved the clients into context rather than in state of the actual page where the table is, but now of course my sort function no longer works. What I need to be able to do is use the same function, but organize the array that is in the context state instead.

    Original function:

    onSortClient = column => e => {
            const direction = this.state.sort.column
                ? this.state.sort.direction === "asc"
                    ? "desc"
                    : "asc"
                : "desc";
            const sortedData = this.state.clients.sort((a, b) => {
                if (column === "client_name") {
                    const nameA = a.client_name.toUpperCase();
                    const nameB = b.client_name.toUpperCase();
                    if (nameA < nameB) {
                        return -1;
                    }
                    if (nameA > nameB) {
                        return 1;
                    }
    
                    return 0;
                }
                return 0;
            });
    
            if (direction === "desc") {
                sortedData.reverse();
            }
    
            this.setState({
                clients: sortedData,
                sort: {
                    column,
                    direction
                }
            });
        };
    

    My context file:

    import React, { Component } from "react";
    import axios from "axios";
    
    const Context = React.createContext();
    
    const Reducer = (state, action) => {
        switch (action.type) {
            case "DELETE_CLIENT":
                console.log(action.payload);
                return {
                    ...state,
                    clients: state.clients.filter(client => client.id !== action.payload)
                };
            case "ADD_CLIENT":
                return {
                    ...state,
                    clients: [action.payload, ...state.clients]
                };
            case "UPDATE_CLIENT":
                console.log(action.payload);
                return {
                    ...state,
                    clients: state.clients.map(
                        client =>
                            client.id === action.payload.id ? (client = action.payload) : client
                    )
                };
    
            default:
                return state;
        }
    };
    
    export class Provider extends Component {
        state = {
            clients: [],
            loaded: false,
            dispatch: action => {
                this.setState(state => Reducer(state, action));
            }
        };
    
        async componentDidMount() {
            let localToken = localStorage.getItem("iod_tkn");
    
            const res = await axios({
                url: "/users/get_clients",
                method: "get",
                headers: {
                    Authorization: localToken
                }
            });
    
            this.setState({
                clients: res.data,
                loaded: true
            });
        }
    
    
        render() {
            return (
                <Context.Provider onSortClient={this.onSortClient} value={this.state}>
                    {this.props.children}
                </Context.Provider>
            );
        }
    }
    
    export const Consumer = Context.Consumer;
    
  • acd37
    acd37 over 5 years
    If you're pretty good at React, I have another question that nobody has been able to solve yet :) stackoverflow.com/questions/51994498/…
  • user2918201
    user2918201 over 2 years
    I feel it necessary to point out that if you use this code as is, every component that consumes this state will re-render every time this component re-renders. This is because you are creating a new object to pass to value every time this renders. See: reactjs.org/docs/context.html#caveats