【问题标题】:React Context api - Consumer Does Not re-render after context changedReact Context api-上下文更改后消费者不重新渲染
【发布时间】:2019-07-16 19:15:43
【问题描述】:

我搜索了一个答案,但找不到任何答案,所以我在这里问, 我有一个更新上下文的消费者, 和另一个应该显示上下文的消费者。 我正在使用打字稿反应(16.3)

上下文(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);

生产者(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;

更新上下文消费者(SubmitTransactionFile.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;

最后是显示消费者(WithdrawTransactionsTable.tsx):

class WithdrawTransactionsTable extends React.Component {

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

export default WithdrawTransactionsTable;

据我了解,在调用fileLoadedEvent 函数后,context.setJsonTran 应该重新渲染其他消费者,WithdrawTransactionsTable 组件应该重新渲染,但事实并非如此。

我做错了什么?

【问题讨论】:

  • 这可能不是 OP 的原因,但我有一个非常相似的问题。在我的情况下,我不小心在我的一个嵌套组件中包含了另一个 AppContext.Provider。这是一种扭曲的行为,因为任何更深层次的孩子都没有像我所期望的那样与提供者交谈。

标签: javascript reactjs typescript react-context


【解决方案1】:

当您更新状态时,您不会触发 Provider 的重新渲染,因此消费者数据不会改变。您应该使用 setState 更新状态并将上下文值分配给提供者,例如

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;

【讨论】:

    【解决方案2】:

    您的setJsonTran 只是改变了上下文的默认值,这不会导致valueProvider 的值发生变化。

    您可以改为将jsonTransactions 保持在最顶层状态,并传递一个更改此状态的函数,进而更新value

    示例

    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>

    【讨论】:

    • 这有助于我了解我的问题。在我的例子中,我使用我的上下文来表示一个数组。当我尝试推送或拼接数组并在我的 reducer 中返回它时,React 不会将其视为更新。我通过将数组嵌套为上下文状态中的属性来解决我的问题,例如 { myArray: [] }。正是你对“变异”这个词的使用让我明白了我所忽视的……谢谢!
    猜你喜欢
    • 2021-04-08
    • 1970-01-01
    • 2021-11-06
    • 2018-09-30
    • 2018-12-21
    • 2019-06-10
    • 2022-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多