تغییر داده‌ها و render کردن آن‌ها در مرورگر

22 بهمن 1399
تغییر داده ها و render کردن آنها در مرورگر

تغییر داده ها و render کردن آنها در مرورگر

در جلسه ی قبل با استفاده از پکیج Axios و سرور تمرینی JSONPlaceholder توانستیم داده هایی را از سرور دریافت کرده و آن ها را در console مرورگر نمایش دهیم اما console جای مناسبی برای نمایش داده ها نیست. در این جلسه می خواهیم کاری کنیم تا داده های دریافتی از سمت سرور تمرینی را درون مرورگر نمایش دهیم.

برای انجام این کار اول از همه به State نیاز داریم:

    state = {
        posts: []
    }

در ابتدای کار مقدار posts را یک آرایه ی خالی قرار داده ایم و بعدا داده های دریافتی از سمت سرور تمرینی را در آن قرار خواهیم داد. من از شما سوالی دارم؛ به نظر شما در کدام قسمت برنامه از setState استفاده کنیم؟ احتمالا همه ی شما می گویید در componentDidMount و جوابتان صحیح خواهد بود اما منظور من این است که درون متد then یا خارج از آن؟ جواب صحیح داخل متد then و به شکل زیر است:

    componentDidMount() {
        axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
            this.setState();
            console.log(response);
        })

چرا؟ به دلیل نامتقارن بودن جاوا اسکریپت! همانطور که گفتیم جاوا اسکریپت برای دریافت پاسخ از سمت سرور و تکمیل کدها صبر نمی کند بنابراین اگر کد setState را خارج از متد then صدا بزنیم، هنوز شیء promise دریافت نشده (هنوز پاسخ از سمت سرور به ما ارسال نشده) بنابراین اصلا response ای وجود ندارد که بخواهیم آن را به setState بدهیم. کد تکمیل شده ی setState بدین شکل خواهد بود:

    componentDidMount() {
        axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
            this.setState({ posts: response.data });
            // console.log(response);
        })
    }

قبلا شیء response را در console دیده ایم و میدانیم که درون آن data وجود دارد:

قسمت data از شیء دریافتی از سرور تمرینی JSONPlaceholder
قسمت data از شیء دریافتی از سرور تمرینی JSONPlaceholder

حالا باید posts را به صورت پویا در قسمت JSX نمایش دهیم. برای این کار در قسمت render (درون فایل Blog.js) یک ثابت به نام posts تعریف می کنیم:

const posts = this.state.posts.map(post => {
    return <Post />
});

سپس در قسمت JSX کدهای دستی </ Post> را حذف کنیم و آن ها را به صورت پویا نمایش دهیم. بنابراین کل قسمت render بدین شکل خواهد بود:

render() {

    const posts = this.state.posts.map(post => {
        return <Post />
    });

    return (
        <div>
            <section className="Posts">
                {posts}
            </section>
            <section>
                <FullPost />
            </section>
            <section>
                <NewPost />
            </section>
        </div>
    );
}

حالا باید به Post.js برویم و برخی از مقادیر را ویرایش کنیم. به طور مثال درون قسمت JSX یک تگ h1 داریم که فعلا مقدار title در آن قرار گرفته است اما باید در اصل بدین شکل باشد:

 <h1>{props.title}</h1>

یعنی برای دریافت محتوا به صورت پویا باید عنوان را از یک prop دریافت کنیم. مثلا برای همین مورد می توانیم به فایل Blog.js برگشته و  این کار را انجام دهیم:

const posts = this.state.posts.map(post => {
    return <Post title={post.title} />
});

یادتان باشد که درخواست get را به آدرس https://jsonplaceholder.typicode.com/posts ارسال کرده ایم و هر پست در این شیء به صورت زیر است:

  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  }

یعنی دارای خصوصیت های userId و id و title و body است. post.title به همین title اشاره دارد. حالا اگر فایل ها را ذخیره کرده و وارد مرورگر شویم چنین چیزی را خواهیم دید:

نمایش تمام نتایج برگشت داده شده در مرورگر
نمایش تمام نتایج برگشت داده شده در مرورگر

با توجه به تصویر بالا، سه مشکل داریم:

  • استایل دهی مشکل دارد (عناوین طولانی از کادر بیرون زده اند)
  • در قسمت Console با خطای key روبرو می شویم چرا که هنوز آن را تعیین نکرده ایم.
  • تمام 100 مورد عنوان را در مرورگر نمایش داده ایم که بسیار زیاد می باشد.

برای حل مشکل key در همان تابع map (در فایل Blog.js) می گوییم:

const posts = this.state.posts.map(post => {
    return <Post key={post.id} title={post.title} />
});

در شیء برگرانده شده به شما نشان دادم که مقداری به نام id داشتیم که برای key عالی است چرا که یکتا و تکرارناپذیر است.

برای حل مشکل استایل دهی نیز به فایل Post.css می رویم و عرض کلاس Post. را به 250 پیکسل تغییر می دهیم:

.Post {
    width: 250px;
    padding: 16px;
    text-align: center;
    border: 1px solid #eee;
    box-shadow: 0 2px 3px #ccc;
    margin: 10px;
    box-sizing: border-box;
    cursor: pointer;
}

(در کد بالا تنها مقدار width را از 100 پیکسل به 250 پیکسل تغییر داده ایم)

حالا مشکل استایل دهی نیز حل شد و فضای بیشتری برای استایل هایمان داریم. مشکل آخر ما مربوط به تعداد پست ها است. ما عناوین تمام پست ها را در مرورگر چاپ کرده ایم اما می خواهیم فقط تعداد محدودی از داده های دریافتی را چاپ کنیم. راه حل چیست؟

در حالت عادی به راحتی می توانید یک کوئری محدود شده به سرور خود ارسال کنید (مثلا با کوئری های MySQL و دستوراتی مثل limit و پیاده سازی pagination) تا تعداد داده های دریافتی را کمتر کنید اما ما اجازه نداریم به سرور تمرینی خود کوئری خاصی ارسال کنیم (سرور متعلق به ما نیست و به دستورات ما پاسخی نخواهد داد). برای حل این مشکل می توانیم درون متد then (درون componentDidMount) یک ثابت به نام posts ایجاد کنیم و چنین کاری را انجام می دهم:

componentDidMount() {
    axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
        const posts = response.data.slice(0, 4);
        this.setState({ posts: response.data });
        // console.log(response);
    })
}

یعنی داده های دریافتی از سرور را درون ثابت posts قرار داده و سپس متد slice را روی آن ها صدا زدم. این کار باعث می شود که تنها نتایج اول تا چهارم (از ایندکس صفر) در ثابت posts ذخیره شود. حالا یک ثابت دیگر می سازیم تا Author یا همان نام نویسنده را هم به پروژه اضافه کنیم. این مقدار را معمولا از سمت سرور دریافت می کنیم ولی سرور تمرینی ما نام نویسنده را ندارد بنابراین مجبوریم آن را به صورت دستی بسازیم:

componentDidMount() {
    axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
        const posts = response.data.slice(0, 4);
        const updatedPosts = posts.map(post => {
            return {
                ...post,
                author: 'Max'
            }
        });
        this.setState({ posts: response.data });
        // console.log(response);
    })
}

در واقع posts را با متد map ویرایش کرده ایم تا برای هر post یک شیء جدید جاوا اسکریپت را برگردانیم. این شیء جدید حاوی خصوصیات post به صورت تجزیه شده (استفاده از اپراتور spread - همان علامت سه نقطه) و خصوصیتی به نام author (نویسنده) است که مقدارش را Max گذاشته ایم. شما می توانید هر نام دیگری برای نویسنده ی خود انتخاب کنید.

در آخر باید مقدار posts در state را برابر با updatedPosts قرار دهیم:

componentDidMount() {
    axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
        const posts = response.data.slice(0, 4);
        const updatedPosts = posts.map(post => {
            return {
                ...post,
                author: 'Max'
            }
        });
        this.setState({ posts: updatedPosts });
        // console.log(response);
    })
}

سپس به متد map می رویم (پایین تر) و مقدار author را نیز به عنصر <Post /> اضافه می کنیم:

render() {

    const posts = this.state.posts.map(post => {
        return <Post key={post.id} title={post.title} author={post.author} />
    });

// بقیه ی کدها //

حالا می توانیم با مراجعه به Post.js نام نویسنده را نیز تغییر دهیم:

const post = (props) => (
    <article className="Post">
        <h1>{props.title}</h1>
        <div className="Info">
            <div className="Author">{props.author}</div>
        </div>
    </article>
);

حالا اگر به مرورگر برویم، مشکلی را نخواهیم دید:

نمایش نتایج برگشتی به صورت محدود شده توسط جاوااسکریپت
نمایش نتایج برگشتی به صورت محدود شده توسط جاوا اسکریپت

بسته به اندازه ی صفحه ی مانیتور شما ممکن است 4 یا تعداد کمتر و بیشتری از پست ها نمایش داده شود (این مورد به استایل دهی مربوط است) بنابراین اگر تصویر شما دقیقا مانند تصویر بالا نبود جای نگرانی نیست. امیدوارم این قسمت برای شما مفید بوده باشد.

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

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

erfan
02 فروردین 1401
حط چهار این مکد را اشتباه نوشتید componentDidMount() { axios.get('https://jsonplaceholder.typicode.com/posts').then(response => { const posts = response.data.slice(0, 4); this.setState({ posts: response.data }); // console.log(response); }) } باید باشه this.setState({ posts: posts });

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