CanUver
CanUver

Reputation: 1774

How can ı pass function with state for react useContext

Trying to learn how to use react hooks. I use createContext and I want to transfer together state and a function to other components, but I need help with how to do this. I also want to ask if it makes sense to do so.

In fact, I can update state by using setState in my case, but by defining a function in context, I think it makes more sense to use this function anywhere. Am I wrong ?

my context is :

export const ExpenseContext = createContext();

export const ExpenseProvider = props => {
  const [expense, setExpense] = useState(initialExpenses)

  const clearItems = () => {
    setExpense([])
  }

  return ( <
    ExpenseContext.Provider value = {
      [expense, setExpense],
      clearItems
    } > {
      props.children
    } <
    /ExpenseContext.Provider>
  )
}

The component I want to use this function :

const ExpenseList = () => {
  const [expense, setExpense] = useContext(ExpenseContext);
  const {
    clearItem
  } = useContext(ExpenseContext);

  //   const clearItems = () => { normally this works ı know! 
  //     setExpense([])
  // }

  return ( <
    >
    <
    List > {
      expense.map((expense) => {
        return <Item key = {
          expense.id
        }
        expense = {
          expense
        }
        />;
      })
    } <
    /List> <
    div className = "text-center" > {
      expense.length > 0 && ( <
        Button onClick = {
          clearItems
        }
        type = "danger"
        shape = "round"
        icon = { < DeleteOutlined / >
        } >
        Clear Expenses <
        /Button>
      )
    } <
    /div> < /
    >
  );
};

in this case, it is possible to use this function directly within the component without trying to transfer the function. But what I'm curious about is how I can manage to transfer and receive functions.

Note: when I try to use it in this way, I get this error: TypeError: Object is not a function

Upvotes: 6

Views: 22760

Answers (2)

ludwiguer
ludwiguer

Reputation: 2245

How I said in the comment you're very close, you pass functions, objects and values the same way

  const [expenses, setExpenses] = useState([]);
  const clearItems = () => {
    setExpenses([]);
  };
  const value = { expenses, setExpenses, clearItems };

Upvotes: 4

Justin Mitchell
Justin Mitchell

Reputation: 675

The React Context is a mechanism for passing data through a React node tree without having to pass the props manually. The use of createContext creates a reference for a data structure within React's ecosystem that exposes a given data structure to the children nodes.

Context.Provider on the other hand provides a value to consuming components that are children nodes of the Provider. There are a few caveats to remember - whenever the properties of the value your provider is changing, it will trigger a re-render in all of its subscribers - it's descendants. The provider itself is not a hook, therefore when using hooks to generate values, you must re-render the provider with the new values set.


There are several things in your code that need to be addressed:

  1. Using useState in a Context.Provider,
  2. Returning the result of useState as the provider value, and
  3. The structure of data provided as the value to provider

Using useState in a Context.Provider

When consuming the result of useState within a react Context, you have to be aware of the implications of consumption in descendants, and the impact that it will have. React is very good at determining what to re-render and when, and it provides you with control to limit that behavour even more. useState however, is a react Functional Component Hook that will trigger a new render every time the value is updated. There are rules around this, precedence and delays, but you are guaranteed a re-render of whatever Functional Component consumes the useState hook. It's for this reason you want to treat your providers/context objects as pure functions where possible.

Returning the result of useState as the provider value

Within your example, you return the result of the useState call as-is to your context provider. This gives an object structure that React can't properly subscribe to and listen for changes.

The structure of data provided as the value to provider

When using state with your Provider, you need to return as proper JSON to the provider, so that that it can subscribe to value changes, and notify its descendants

Use

const providerValue = {
  expense,
  setExpense,
  clearItems
};
<ExpenseContext.Provider value={providerValue}/>

Instead of:

<ExpenseContext.Provider value = {
  [expense, setExpense],
  clearItems
} .../>

The object structure that you're providing the context provider is invalid. See example codepen.

Check out this great example on dev.to for useState with Context

Upvotes: 11

Related Questions