استفاده از Ref ها (ارجاع) در React

React Ref

23 بهمن 1399
استفاده از Ref ها (ارجاع) در react

ارجاع یا Ref در react چیست؟

در ادامه این فصل و بررسی عمیق مسائل پشت پرده در react به مبحث ref ها می رسیم. احتمالا حس می کنید که مسائل بسیار زیادی را بررسی کرده ایم و حفظ کردن آن ها بسیار دشوار خواهد بود. در این باره می توانم به شما اطمینان بدهم که جای نگرانی نیست، در آینده که بر روی پروژه اصلی خود کار کنیم و همچنین در طول این دوره بارها و بارها از این قابلیت ها استفاده خواهیم کرد. بنابراین شما به طور ناخودآگاه تمام آن ها را حفظ خواهید کرد.

 برای شروع بحث امروز فایل Person.js را باز کنید:

        return (
            <Aux>
                <p key="i1" onClick={this.props.click}>I'm {this.props.name} and I am {this.props.age} years old!</p>
                <p key="i2">{this.props.children}</p>
                <input
                key="i3"
                type="text"
                onChange={this.props.changed} value={this.props.name} />
            </Aux>
        );

در این فایل یک input داریم که نام کاربر را تغییر می دهد. نحوه کدنویسی آن نیز بدون مشکل است اما باید با نکته مهمی آشنا شویم: زمانی که با کدهای JSX (که بعدا تبدیل به HTML می شوند) سروکار دارید، two way binding (روش فعلی) تنها روش ممکن نیست. فرض کنید می خواهیم پس از render شدن صفحه، نفر آخر (که در پروژه ما Stephanie است) به صورت خودکار دارای focus شود (یعنی input برایش فعال باشد). در چنین حالتی کار ما بسیار سخت می شود؛ چطور می خواهیم با کدهای بالا عنصر را focus بدهیم؟

در جاوا اسکریپت عادی می توانیم بگوییم:

class Person extends Component {

    componentDidMount() {
        document.querySelector('input').focus();
    }
	// بقیه کدها

اگر این کد را ذخیره کنیم و به مرورگر برویم، نفر اول برنامه (Max) را دارای focus می بینیم. بله می توانیم روی عنصر آخر id گذاشته و با querySelector آن را انتخاب کنیم اما این روش اصلا بهینه نیست. React یک روش بسیار آسان تر برای این کار دارد: Ref (مخفف reference و به معنی «ارجاع»).

شما می توانید روی هر عنصری (هر عنصر JSX یا حتی کامپوننت های خودتان) از یک کلیدواژه خاص به نام ref استفاده کنید. ref مانند key یک خصوصیت خاص و رزرو شده در react است. روش های مختلفی برای استفاده از ref وجود دارد؛ روش اول که در نسخه های قدیمی react هم پشتیبانی می شود، پاس دادن یک تابع به Ref است؛ آرگومان دریافتی این تابع، یک ارجاع (reference) به عنصری است که آن را دریافت کرده است:

        return (
            <Aux>
                <p key="i1" onClick={this.props.click}>I'm {this.props.name} and I am {this.props.age} years old!</p>
                <p key="i2">{this.props.children}</p>
                <input
                key="i3"
                ref={(inputEl) => {}}
                type="text"
                onChange={this.props.changed} value={this.props.name} />
            </Aux>
        );

ما در این قسمت نام آن را inputEl گذاشته ایم که به همین عنصر input اشاره دارد اما شما می توانید هر نامی که دلخواه خودتان بود را برای آن انتخاب کنید. حالا می توانید از این ارجاع در بدنه تابع استفاده کنید:

ref={(inputEl) => {inputEl.focus()}}

این کد به طور صحیح کار می کند اما من می خواهم کار بهتری انجام بدهم:

ref={(inputEl) => {this.inputElement = inputEl}}

در اینجا this به کلاس Person اشاره دارد. شما می توانید هر نام دیگری را به جای inputElement انتخاب کنید. از این به بعد هر جای برنامه می توانیم از inputElement (خصوصیت سراسری جدیدمان) استفاده کنیم. از طرفی می دانیم که componentDidMount بعد از متد render اجرا می شود بنابراین inputElement در آن قابل استفاده است و می توان گفت:

    componentDidMount() {
        this.inputElement.focus();
    }

در واقع کدهای تمام کلاس ما بدین شکل است:

class Person extends Component {

    componentDidMount() {
        this.inputElement.focus();
    }
    render() {
        console.log('[Person.js rendering...');
        return (
            <Aux>
                <p key="i1" onClick={this.props.click}>I'm {this.props.name} and I am {this.props.age} years old!</p>
                <p key="i2">{this.props.children}</p>
                <input
                    key="i3"
                    ref={(inputEl) => { this.inputElement = inputEl }}
                    type="text"
                    onChange={this.props.changed} value={this.props.name}
                />
            </Aux>
        );
    }

};

اگر حالا به مرورگر برویم، عنصر آخر دارای focus خواهد بود. واضح است که این روش تنها در کامپوننت های کلاس-محور قابل استفاده است. از نسخه 16.3 کتابخانه react، روش جدیدی برای استفاده از ref ها داریم: استفاده از constructor!

    constructor () {
        this.inputElementRef = React.createRef();
    }

بله در این حالت ref ایجاد شده توسط react هر شیء ref ای است که react به ما بدهد و دیگر به عنصر خاصی که ما می خواهیم اشاره نمی کند. حالا می توانیم در قسمت JSX بگوییم:

        return (
            <Aux>
                <p key="i1" onClick={this.props.click}>I'm {this.props.name} and I am {this.props.age} years old!</p>
                <p key="i2">{this.props.children}</p>
                <input
                    key="i3"
                    // ref={(inputEl) => { this.inputElement = inputEl }}
                    ref={this.inputElementRef}
                    type="text"
                    onChange={this.props.changed} value={this.props.name}
                />
            </Aux>
        );

بدین صورت react می فهمد که خصوصیت ایجاد شده در constructor (همان inputElementRef) مربوط به عنصر input من است (چرا که آن را به input دادم).

بنابراین می توانیم در قسمت componentDidMount بگوییم:

    componentDidMount() {
        // this.inputElement.focus();
        this.inputElementRef.current.focus();
    }

برای دسترسی به آن حتما باید از current استفاده کنید تا بتوانید به ارجاع یا ref فعلی دسترسی داشته باشید. در واقع current همان خط زیر است:

ref={this.inputElementRef}

یعنی همان عنصر input؛ بنابراین دارای متد focus خواهد بود.

اما هنوز کد ما یک مشکل کوچک دارد که در جلسات قبل در مورد آن بحث کرده بودیم. می خواهم خودتان سعی کنید و مشکل کد زیر را پیدا کنید:

class Person extends Component {

    constructor() {
        this.inputElementRef = React.createRef();
    }

    componentDidMount() {
        // this.inputElement.focus();
        this.inputElementRef.current.focus();
    }
    render() {
        console.log('[Person.js rendering...');
        return (
            <Aux>
                <p key="i1" onClick={this.props.click}>I'm {this.props.name} and I am {this.props.age} years old!</p>
                <p key="i2">{this.props.children}</p>
                <input
                    key="i3"
                    // ref={(inputEl) => { this.inputElement = inputEl }}
                    ref={this.inputElementRef}
                    type="text"
                    onChange={this.props.changed} value={this.props.name}
                />
            </Aux>
        );
    }

};

بله، مشکل در constructor است. ما قبلا گفته بودیم که اگر بخواهیم از constructor در react استفاده کنیم حتما باید props را به آن پاس داده و همچنین تابع ()super را صدا بزنیم:

    constructor(props) {
        super(props);
        this.inputElementRef = React.createRef();
    }

در غیر این صورت با مشکل مواجه خواهیم شد.

حالا می توانید به مرورگر برویم و خواهید دید که هنوز هم نفر آخر دارای focus است. بنابراین شما می توانید از هر کدام از این دو روش استفاده کنید؛ روش اول که روش قدیمی تری است و با یک تابع ساده کار می کند و یا استفاده از روش دوم که مدرن تر است و از constructor کمک میگیرد. مبحث Ref ها مبحثی مهم و کاربردی است بنابراین بهتر است آن را تمرین کنید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.