【问题标题】:React: set State or set Prop without a RerenderReact:设置 State 或设置 Prop 而不使用 Rerender
【发布时间】:2019-07-03 15:25:44
【问题描述】:

问题:

目前,我有一个LoginForm 组件,它有一个“成功”处理函数handleOnSuccess。然后使用由“令牌更新”处理函数handleUpdateToken 定义的onTokenUpdate 属性链接到父组件。问题是handleUpdateToken 函数中的setState 正在强制进行不希望的重新渲染。

期望的结果:

我最终需要的是使用成功获得的值更新LoginForm 组件属性token,而不执行重新渲染。这甚至可能吗?根据React: Update Child Component Without Rerendering Parent 的说法,这似乎不是,但是,对于我的情况,没有可行的替代方案被建议。如果这是不可能的,我想知道是否有人有任何建议的替代方案。

代码

LoginForm.react.js:

import React, { Component } from 'react';
import Script from 'react-load-script';
import PropTypes from 'prop-types';


class LoginForm extends Component {
    constructor(props) {
        super(props);

        this.state = {
            linkLoaded: false,
            initializeURL: 'https://cdn.plaid.com/link/v2/stable/link-initialize.js',
        };

        this.onScriptError = this.onScriptError.bind(this);
        this.onScriptLoaded = this.onScriptLoaded.bind(this);

        this.handleLinkOnLoad = this.handleLinkOnLoad.bind(this);

        this.handleOnExit = this.handleOnExit.bind(this);
        this.handleOnEvent = this.handleOnEvent.bind(this);
        this.handleOnSuccess = this.handleOnSuccess.bind(this);

        this.renderWindow = this.renderWindow.bind(this);
    }

    onScriptError() {
        console.error('There was an issue loading the link-initialize.js script');
    }

    onScriptLoaded() {
        window.linkHandler = window.Plaid.create({
            apiVersion: this.props.apiVersion,
            clientName: this.props.clientName,
            env: this.props.env,
            key: this.props.publicKey,
            onExit: this.handleOnExit,
            onLoad: this.handleLinkOnLoad,
            onEvent: this.handleOnEvent,
            onSuccess: this.handleOnSuccess,
            product: this.props.product,
            selectAccount: this.props.selectAccount,
            token: this.props.token,
            webhook: this.props.webhook,
        });

        console.log("Script loaded");
    }

    handleLinkOnLoad() {
        console.log("loaded");
        this.setState({ linkLoaded: true });
    }
    handleOnSuccess(token, metadata) {
        console.log(token);
        console.log(metadata);
        this.props.onTokenUpdate(token);
    }
    handleOnExit(error, metadata) {
        console.log('link: user exited');
        console.log(error, metadata);
    }
    handleOnLoad() {
        console.log('link: loaded');
    }
    handleOnEvent(eventname, metadata) {
        console.log('link: user event', eventname, metadata);
    }

    renderWindow() {
        const institution = this.props.institution || null;
        if (window.linkHandler) {
            window.linkHandler.open(institution);
        }
    }

    static exit(configurationObject) {
        if (window.linkHandler) {
            window.linkHandler.exit(configurationObject);
        }
    }

    render() {
        return (
            <div id={this.props.id}>
                {this.renderWindow()}
                <Script
                    url={this.state.initializeURL}
                    onError={this.onScriptError}
                    onLoad={this.onScriptLoaded}
                />
            </div>
        );
    }
}

LoginForm.defaultProps = {
    apiVersion: 'v2',
    env: 'sandbox',
    institution: null,
    selectAccount: false,
    style: {
        padding: '6px 4px',
        outline: 'none',
        background: '#FFFFFF',
        border: '2px solid #F1F1F1',
        borderRadius: '4px',
    },
};

LoginForm.propTypes = {
    // id
    id: PropTypes.string,

    // ApiVersion flag to use new version of Plaid API
    apiVersion: PropTypes.string,

    // Displayed once a user has successfully linked their account
    clientName: PropTypes.string.isRequired,

    // The Plaid API environment on which to create user accounts.
    // For development and testing, use tartan. For production, use production
    env: PropTypes.oneOf(['tartan', 'sandbox', 'development', 'production']).isRequired,

    // Open link to a specific institution, for a more custom solution
    institution: PropTypes.string,

    // The public_key associated with your account; available from
    // the Plaid dashboard (https://dashboard.plaid.com)
    publicKey: PropTypes.string.isRequired,

    // The Plaid products you wish to use, an array containing some of connect,
    // auth, identity, income, transactions, assets
    product: PropTypes.arrayOf(
        PropTypes.oneOf([
            // legacy product names
            'connect',
            'info',
            // normal product names
            'auth',
            'identity',
            'income',
            'transactions',
            'assets',
        ])
    ).isRequired,

    // Specify an existing user's public token to launch Link in update mode.
    // This will cause Link to open directly to the authentication step for
    // that user's institution.
    token: PropTypes.string,
    access_token: PropTypes.string,

    // Set to true to launch Link with the 'Select Account' pane enabled.
    // Allows users to select an individual account once they've authenticated
    selectAccount: PropTypes.bool,

    // Specify a webhook to associate with a user.
    webhook: PropTypes.string,

    // A function that is called when a user has successfully onboarded their
    // account. The function should expect two arguments, the public_key and a
    // metadata object
    onSuccess: PropTypes.func,

    // A function that is called when a user has specifically exited Link flow
    onExit: PropTypes.func,

    // A function that is called when the Link module has finished loading.
    // Calls to plaidLinkHandler.open() prior to the onLoad callback will be
    // delayed until the module is fully loaded.
    onLoad: PropTypes.func,

    // A function that is called during a user's flow in Link.
    // See
    onEvent: PropTypes.func,


    onTokenUpdate: PropTypes.func,

    // Button Styles as an Object
    style: PropTypes.object,

    // Button Class names as a String
    className: PropTypes.string,
};

export default LoginForm;

App.js:

// /* eslint no-magic-numbers: 0 */
import React, { Component } from 'react';
import { LoginForm } from '../lib';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            access_token: null
        };
        this.handleUpdateToken = this.handleUpdateToken.bind(this)
    }

    handleUpdateToken(access_token) {
        this.setState({ access_token: access_token });
    }

    render() {
        return (
            <LoginForm
                id="Test"
                clientName="Plaid Client"
                env="sandbox"
                product={['auth', 'transactions']}
                publicKey="7a3daf1db208b7d1fe65850572eeb1"
                className="some-class-name"
                apiVersion="v2"
                onTokenUpdate={this.handleUpdateToken}
                token={this.state.access_token}
            >
            </LoginForm>
        );
    }
}

export default App;

提前感谢您的任何/所有帮助!

【问题讨论】:

  • 可以使用state of &lt;LoginForm/&gt;中的token
  • 你是什么意思?为此,必须将令牌值分配给 LoginFormproperty,而不是状态 - 这就是您的意思吗?
  • 您需要access_token 才能加入App。从您的示例中,除了将其传递给 LoginForm 之外,您什么也不做
  • @Sterling Butters 您想对access_token 中的&lt;App /&gt; 进行一些更改吗?
  • @MohamedRamrami 是的,它必须在 App 中才能正确构建 Dash 组件

标签: javascript reactjs components rendering setstate


【解决方案1】:

如果要更新子级的 props,则无法阻止呈现 parent。但是有办法实现它。

  1. 您需要将token 存储在您的LoginForm 状态中。
  2. 你需要在componentWillReceiveProps中更改LoginFormstate
  3. 您需要在this.props.onTokenUpdate(token,function); 中传递tokenfunction。此函数将有一个参数是来自父级的令牌。在function 中,您将更改state。(如果您想更改父组件中的令牌并发送更新的令牌,则需要这样做)。
  4. 如果要阻止 render(),则父级中的 token 不应位于 state 中。它应该是组件属性
  5. 使用this.state.token而不是this.props.token

以下是一个一般示例。 Codepen

【讨论】:

  • 在聊天中回复
  • 您有机会查看我的具体案例吗?我在聊天中提供了一些更具体的细节
  • @Sterling Butters 我已经看到了。伙计,实际上我不能,因为从第二天起我的考试就要到了。请检查代码笔。就这么简单
  • 哈哈,它可能适合你。嗯,祝你考试顺利,谢谢你的帮助!也许如果我在你完成考试时还没有弄清楚,我会再次联系
【解决方案2】:

这就是我最终要做的(但我接受了@MaheerAli 的回答,因为有些人可能一直在寻找它):

LoginForm.js:

class LoginForm extends Component {
    constructor(props) {
        super(props);

        this.state = {
            linkLoaded: false,
            initializeURL: 'https://cdn.plaid.com/link/v2/stable/link-initialize.js',
        };

        this.onScriptError = this.onScriptError.bind(this);
        this.onScriptLoaded = this.onScriptLoaded.bind(this);

        this.handleLinkOnLoad = this.handleLinkOnLoad.bind(this);

        this.handleOnExit = this.handleOnExit.bind(this);
        this.handleOnEvent = this.handleOnEvent.bind(this);
        this.handleOnSuccess = this.handleOnSuccess.bind(this);

        this.renderWindow = this.renderWindow.bind(this);
    }

    onScriptError() {
        console.error('There was an issue loading the link-initialize.js script');
    }

    onScriptLoaded() {
        window.linkHandler = window.Plaid.create({
            apiVersion: this.props.apiVersion,
            clientName: this.props.clientName,
            env: this.props.env,
            key: this.props.publicKey,
            onExit: this.handleOnExit,
            onLoad: this.handleLinkOnLoad,
            onEvent: this.handleOnEvent,
            onSuccess: this.handleOnSuccess,
            product: this.props.product,
            selectAccount: this.props.selectAccount,
            token: this.props.token,
            webhook: this.props.webhook,
        });
    }

    handleLinkOnLoad() {
        console.log("loaded");
        this.setState({ linkLoaded: true });
    }
    handleOnSuccess(token, metadata) {
        console.log(token);
        console.log(metadata);
        this.props.onTokenUpdate(token);
    }
    handleOnExit(error, metadata) {
        console.log('PlaidLink: user exited');
        console.log(error, metadata);
    }
    handleOnLoad() {
        console.log('PlaidLink: loaded');
    }
    handleOnEvent(eventname, metadata) {
        console.log('PlaidLink: user event', eventname, metadata);
    }

    renderWindow() {
        const institution = this.props.institution || null;
        if (window.linkHandler) {
            window.linkHandler.open(institution);
        }
    }

    chooseRender() {
        if (this.props.access_token === null) {
            this.renderWindow()
        }
    }

    static exit(configurationObject) {
        if (window.linkHandler) {
            window.linkHandler.exit(configurationObject);
        }
    }

    render() {
        return (
            <div id={this.props.id}
                 access_token={this.props.access_token}>
                {this.chooseRender()}
                <Script
                    url={this.state.initializeURL}
                    onError={this.onScriptError}
                    onLoad={this.onScriptLoaded}
                />
            </div>
        );
    }
}

App.js:

// /* eslint no-magic-numbers: 0 */
import React, { Component } from 'react';
import { LoginForm } from '../lib';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            access_token: null
        };
        this.handleUpdateToken = this.handleUpdateToken.bind(this)
    }

    handleUpdateToken(access_token) {
        this.setState({ access_token: access_token });
    }

    render() {
        return (
            <LoginForm
                id="Test"
                access_token={this.state.access_token}

                clientName="Plaid Client"
                env="sandbox"
                product={['auth', 'transactions']}
                publicKey="7a3daf1db208b7d1fe65850572eeb1"
                className="some-class-name"
                apiVersion="v2"
                onTokenUpdate={this.handleUpdateToken}
            >
            </LoginForm>
        );
    }
}

export default App;

最值得注意的是,我刚刚定义了一个 chooseRender 函数,用于选择是否在 Child 中渲染 Plaid。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 2020-08-23
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多