简体   繁体   中英

Passing useState using createContext in React.tsx

I have defined a context Transaction which takes an object and a function. In AppProvider Transaction.Provider is returned. The code is of GlobalState.tsx file:

import { createContext, useState } from "react";

export interface IProviderProps {
    children?: any;
}

type Cat = {
    id: number;
    text: string;
    amount: number;
}

type Ca =Cat[]
export const initialState = {
    state: [
        {id:4, text:'hi', amount:234},
        {id:3, text:'hd', amount:-234},
        {id:1, text:'hs', amount:34}
    ],
    setState: (state: Ca) => {}
}
console.log(initialState.state)
export const Transaction = createContext(initialState);

export const AppProvider = (props: IProviderProps) => {
    const [state, setState] = useState(initialState.state);
    console.log(state);
    return <Transaction.Provider value={{state, setState}}>{props.children}</Transaction.Provider>;
  };

In App.tsx I have passed the Provider:

import React, { useState } from 'react';
import './App.css';
import { Header } from "./Components/Header";
import { Balance } from './Components/Balance';
import { IncomeExpense } from "./Components/Income_Expense";
import { TransactionHistory } from "./Components/TransactionHistory";
import { AddTransaction } from "./Components/AddTransaction";
import { AppProvider } from './Context/GlobalState'

function App() {

  const [islit, setlit] = useState(true);
  

  return (
    <AppProvider>
      <div className={`${islit? '': 'dark'} body`}>
        <Header islit={islit} setlit={setlit} />
        <div className="container">
          <Balance />
          <IncomeExpense />
          <TransactionHistory />
          <AddTransaction />
        </div>
      </div>
    </AppProvider>
  );
}

export default App;

I am trying to change 'state' with 'setState' but it is not working:

import React, { useState, useContext } from 'react';
import { Transaction} from '../Context/GlobalState';

export const AddTransaction = () => {

  const initialState = useContext(Transaction);
  const [Incexp, setIncExp] = useState('income');
  const [text, settext] = useState('');
  const [amount, setamount] = useState(0);
  const transactions = initialState.state;
  const settransaction = initialState.setState;

  function Addition(e: any) {
    e.preventDefault();
    settext('');
    setamount(0);
    transactions.push({id:Math.floor(Math.random() * 100000000), text:text, amount:Incexp==='income'? +amount: -amount})
    settransaction(transactions);
    console.log(transactions);
  }

  return (
    <div>
      <h3>Add Transaction</h3>
      <form onSubmit={Addition}>
        <label htmlFor="description">Text</label>
        <input type="text" id="description" placeholder="Enter description..." value={text} onChange={(e) => { settext(e.target.value) }} required />

        <label htmlFor="amount">Amount</label>
        <input type="number" id="amount" placeholder="Enter Amount..." value={amount === 0 ? '' : amount} onChange={(e) => { setamount(parseInt(e.target.value)) }} required />
        <div className="Inc-Exp">
          <div>
            <input type="radio" id="income" name="balance" defaultChecked onClick={()=>{setIncExp('income')}}/>
            <label htmlFor="income" className="inc-col">Income</label>
          </div>
          <div>
            <input type="radio" id="expense" name="balance" onClick={()=>{setIncExp('expense')}}/>
            <label htmlFor="expense" className="exp-col">Expense</label>
          </div>
        </div>
        <input className="btn" type="submit" value="Addtransaction" />
      </form>
    </div>
  )
}

Another child component:

import React, { useContext } from 'react';
import { Transaction } from '../Context/GlobalState';

export const Balance = () => {

    const initialState = useContext(Transaction);
    const transactions = initialState.state;
    var total=0;
    transactions.map((transaction) => total+=transaction.amount)

    return (
        <div>
            <h4>Your Balance</h4>
            <h1 className={`${total > 0 ? 'plus' : ''} ${total < 0 ? 'minus' : ''}`}>${total}</h1>
        </div>
    )
}

Every time I click on a button Add Transaction. I want it to update state. but it is not updating.

Please change setState to a callback variant setState((previousState) => {...}) as:

function Addition(e: any) {
    e.preventDefault();
    settext('');
    setamount(0);
    transactions.push({id:Math.floor(Math.random() * 100000000), text:text, amount:Incexp==='income'? +amount: -amount})
    settransaction(transactions);
    console.log(transactions);
  }

to

function Addition(e: any) {
    e.preventDefault();
    settext('');
    setamount(0);
    settransaction((prevState) => {
return prevState.push({id:Math.floor(Math.random() * 100000000), text:text, amount:Incexp==='income'? +amount: -amount});
    });
  }

The console.log may not show updated value, as re-render would be required to get updated context value.

But after context update a re-render would going to be trigger by react , and thus the component responsible to show updated state will eventually display the updated state.

The state is not update because you are not changing the reference of the object transactions, do it like below

  function Addition(e: any) {
    e.preventDefault();
    settext('');
    setamount(0);
    settransaction([...transactions,{id:Math.floor(Math.random() * 100000000), text:text, amount:Incexp==='income'? +amount: -amount} ]);
    console.log(transactions);
  }

In this case the object you pass to settransaction will have a new reference the react will update the state

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM