Szukałem odpowiedzi, ale nie mógł znaleźć żadnych, więc pytam tutaj, mam konsumenta, który aktualizuje kontekst, a inny konsument, który powinien wyświetlić kontekst. Używam reakcji z maszynopisami (16.3)

Kontekst (appcontext.tsx):

export interface AppContext {
    jsonTransactions: WithdrawTransactionsElement | null;
    setJsonTran(jsonTransactions: WithdrawTransactionsElement | null): void;
}

export const appContextInitialState : AppContext = {
    jsonTransactions: null,
    setJsonTran : (data: WithdrawTransactionsElement) => {
        return appContextInitialState.jsonTransactions = data;
    }
};

export const AppContext = React.createContext(appContextInitialState);

Producent (app.tsx):

interface Props {}

class App extends React.Component<Props, AppContext> {

  state: AppContext = appContextInitialState;

  constructor(props : Props) {
    super(props);
  }

  render() {
    return (
        <AppContext.Provider value={this.state}>
          <div className="App">
            <header className="App-header">
              <SubmitTransactionFile/>
              <WithdrawTransactionsTable />
            </header>
          </div>
        </AppContext.Provider>
    );
  }
}

export default App;

Konsument kontekstu aktualizacji (submitransactionFile.tsx)

class SubmitTransactionFile extends React.Component {

    private fileLoadedEvent(file: React.ChangeEvent<HTMLInputElement>, context: AppContext): void{
        let files = file.target.files;
        let reader = new FileReader();
        if (files && files[0]) {
            reader.readAsText(files[0]);
            reader.onload = (json) =>  {
                if (json && json.target) {
                    // @ts-ignore -> this is because result field is not recognized by typescript compiler
                    context.setJsonTran(JSON.parse(json.target.result))
                }
            }
        }
    }

    render() {
        return (
            <AppContext.Consumer>
                { context  =>
                    <div className="SubmitTransactionFile">
                        <label>Select Transaction File</label><br />
                        <input type="file" id="file" onChange={(file) =>
                            this.fileLoadedEvent(file, context)} />
                        <p>{context.jsonTransactions}</p>
                    </div>
                }
            </AppContext.Consumer>
        )
    }
}


export default SubmitTransactionFile;

I wreszcie wyświetlany konsument (wycofać transakcję stabilną.tsx):

class WithdrawTransactionsTable extends React.Component {

    render() {
        return (
            <AppContext.Consumer>
                { context  =>
                    <div>
                        <label>{context.jsonTransactions}</label>
                    </div>
                }
            </AppContext.Consumer>
        )
    }
}

export default WithdrawTransactionsTable;

To moje zrozumienie, że po funkcji fileLoadedEvent nazywana jest context.setJsonTran powinna ponowna renderować innych konsumentów, a składnik {x2}} powinien zostać ponownie renderowany, ale nie.

Co ja robię źle?

15
Roie Beck 22 luty 2019, 19:33

2 odpowiedzi

Najlepsza odpowiedź

Po zaktualizowaniu stanu nie wywołujesz ponownego renderowania dostawcy, a zatem dane konsumentów nie zmieniają się. Powinieneś zaktualizować stan za pomocą SetState i przypisać wartość kontekstową do dostawcy

class App extends React.Component<Props, AppContext> {
  constructor(props : Props) {
    super(props);
    this.state = {
         jsonTransactions: null,
         setJsonTran: this.setJsonTran
    };
  }

  setJsonTran : (data: WithdrawTransactionsElement) => {
        this.setState({
             jsonTransactions: data
        });
  }

  render() {
    return (
        <AppContext.Provider value={this.state}>
          <div className="App">
            <header className="App-header">
              <SubmitTransactionFile/>
              <WithdrawTransactionsTable />
            </header>
          </div>
        </AppContext.Provider>
    );
  }
}

export default App;
6
Roie Beck 22 luty 2019, 17:11

Twój setJsonTran Wystarczy zamężować domyślną wartość kontekstu, który nie spowoduje zmianę value do zmiany {x2}}.

Możesz zamiast tego utrzymywać jsonTransactions w najwyższym stanie i przekazać funkcję, która zmieni ten stan i z kolei aktualizuje value.

Przykład

const AppContext = React.createContext();

class App extends React.Component {
  state = {
    jsonTransactions: null
  };

  setJsonTran = data => {
    this.setState({ jsonTransactions: data });
  };

  render() {
    const context = this.state;
    context.setJsonTran = this.setJsonTran;

    return (
      <AppContext.Provider value={context}>
        <div className="App">
          <header className="App-header">
            <SubmitTransactionFile />
            <WithdrawTransactionsTable />
          </header>
        </div>
      </AppContext.Provider>
    );
  }
}
const AppContext = React.createContext();

class App extends React.Component {
  state = {
    jsonTransactions: null
  };

  setJsonTran = data => {
    this.setState({ jsonTransactions: data });
  };

  render() {
    const context = this.state;
    context.setJsonTran = this.setJsonTran;

    return (
      <AppContext.Provider value={context}>
        <div className="App">
          <header className="App-header">
            <SubmitTransactionFile />
            <WithdrawTransactionsTable />
          </header>
        </div>
      </AppContext.Provider>
    );
  }
}

class SubmitTransactionFile extends React.Component {
  fileLoadedEvent(file, context) {
    let files = file.target.files;
    let reader = new FileReader();
    if (files && files[0]) {
      reader.readAsText(files[0]);
      reader.onload = json => {
        if (json && json.target) {
          // slice just to not output too much in this example
          context.setJsonTran(json.target.result.slice(0, 10));
        }
      };
    }
  }

  render() {
    return (
      <AppContext.Consumer>
        {context => (
          <div className="SubmitTransactionFile">
            <label>Select Transaction File</label>
            <br />
            <input
              type="file"
              id="file"
              onChange={file => this.fileLoadedEvent(file, context)}
            />
            <p>{context.jsonTransactions}</p>
          </div>
        )}
      </AppContext.Consumer>
    );
  }
}

class WithdrawTransactionsTable extends React.Component {
  render() {
    return (
      <AppContext.Consumer>
        {context => (
          <div>
            <label>{context.jsonTransactions}</label>
          </div>
        )}
      </AppContext.Consumer>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>
2
Tholle 22 luty 2019, 16:51