MobX在将项目推入@observable数组时不更新视图(已检查其他解决方案,这里也是新手)

问题描述 投票:0回答:1

我正在学习教程,并在客户端应用程序中使用react + mobx +打字稿,并在后端使用dotnet核心Web API 3.1。一切都在后端运行

我面临的问题是在创建CRUD中。创建后,@ observable活动将更新,但视图不会反映所做的更改。在useEffect中,我正在加载所有活动。创建活动时不会调用它。

再次创建活动,但未在视图上呈现活动。页面刷新时显示。

  1. [activityStore.ts@observable activities@action loadActivities@action createActivity所在的存储文件
  2. [App.tsx是我正在调用useEffect来加载所有活动的文件。
  3. ActivityList.tsx组成列表的组件
  4. [ActivityForm.tsx是创建活动的表单组件

而不是在这里显示我的代码,我认为共享我的GitHub链接会更好:https://github.com/safiullah7/Reactivities

反应代码是:https://github.com/safiullah7/Reactivities/tree/master/client-app

activityStore.ts:

import {observable, action, computed} from 'mobx';
import { createContext } from 'react';
import { IActivity } from '../models/activity';
import agent from '../api/agent';

class ActivityStore {
    @observable activities: IActivity[] = [];
    @observable loadingInitial = false;
    @observable selectedActivity: IActivity | undefined;
    @observable editMode = false;
    @observable submitting = false;

    @action loadActivities = async () => {
        this.loadingInitial = true;
        debugger;
        try {
            let activities = await agent.Activities.list();
            activities.forEach(activity => {
                activity.date = activity.date.split('.')[0];
                this.activities.push(activity);
                });
            this.loadingInitial = false;
        } catch (error) {
            this.loadingInitial = false;
            console.log(error);
        }
    }

    @action createActivity = async (activity: IActivity) => {
        this.submitting = true;
        try {
            debugger;
            await agent.Activities.create(activity);
            let activities = [...this.activities, activity]
            this.activities = activities;
            //this.selectedActivity = activity;
            this.editMode = false;
            this.submitting = false;
        } catch (error) {
            //this.editMode = false;
            this.submitting = false;
            console.log(error);
        }
    }

    @action openCreateForm = () => {
        this.selectedActivity = undefined;
        this.editMode = true;
    }

    @action selectActivity = (id: string) => {
        this.selectedActivity = this.activities.find(a => a.id === id);
        this.editMode = false;
    } 
}

export default createContext(new ActivityStore());

App.tsx:

import React, { useState, useEffect, Fragment, SyntheticEvent, useContext } from 'react';
import { Container } from 'semantic-ui-react'
import { IActivity } from '../models/activity';
import NavBar from '../../features/nav/NavBar';
import ActivityDashboard from '../../features/activities/dashboard/ActivityDashboard';
import agent from '../api/agent';
import LoadingComponent from './LoadingComponent';
import ActivityStore from '../stores/activityStore';
import {observer} from 'mobx-react-lite';

const App = () => {
  const activityStore = useContext(ActivityStore);
  const [activities, setActivities] = useState<IActivity[]>([]);
  const [selectedActivity, setSelectedActivity] = useState<IActivity | null>(null);
  const [editMode, setEditMode] = useState<Boolean>(false);
  const [loading, setLoading] = useState(true);
  const [submitting, setSubmitting] = useState(false);
  const [target, setTarget] = useState('');

  useEffect(() => {
    activityStore.loadActivities();
  }, [activityStore]);
  // to run this once and not infinitly to keep getting the data
  // when state changes, useEffect is called. So it will become infinite loop


  const handleEditActivity = (activity: IActivity) => {
    setSubmitting(true);
    agent.Activities.update(activity).then(() => {
      setActivities([...activities.filter(a => a.id !== activity.id), activity]);
      setSelectedActivity(activity);
      setEditMode(false);
    }).then(() => setSubmitting(false));
  };

  const handleDeleteActivity = (event:SyntheticEvent<HTMLButtonElement>,id: string) => {
    setTarget(event.currentTarget.name);
    setSubmitting(true);
    agent.Activities.delete(id).then(() => {
      setActivities([...activities.filter(a => a.id !== id)]);
    }).then(() => setSubmitting(false));
  };

  if (activityStore.loadingInitial)
    return <LoadingComponent content='Loading activities...'/>
  else
    return (
      <Fragment>
        <NavBar />
        <Container style={{marginTop: '7em'}}>
          <ActivityDashboard
            setEditMode={setEditMode}
            setSelectedActivity={setSelectedActivity}
            editActivity={handleEditActivity}
            deleteActivity={handleDeleteActivity}
            submitting={submitting}
            target={target}
            />
        </Container>
      </Fragment>
    );
}

export default observer(App);

ActivityList.tsx:

import React, { SyntheticEvent, useContext, useEffect } from 'react'
import { Item, Button, Label, Segment } from 'semantic-ui-react';
import { IActivity } from '../../../app/models/activity';
import { observer } from 'mobx-react-lite';
import ActivityStore from '../../../app/stores/activityStore';

interface IProps {
    deleteActivity: (e:SyntheticEvent<HTMLButtonElement>, id: string) => void,
    submitting: boolean,
    target: string
}

const ActivityList: React.FC<IProps> = ({ deleteActivity, submitting, target }) => {

    const activityStore = useContext(ActivityStore);
    let {activities, selectActivity} = activityStore;


    return (
        // floating right in the item. need to clear other floats
        <Segment clearing>
            <Item.Group divided>
                {activities.map(activity => {
                    return (
                        <Item key={activity.id}>
                            <Item.Content>
                                <Item.Header as='a'>{activity.title}</Item.Header>
                                <Item.Meta>{activity.date}</Item.Meta>
                                <Item.Description>
                                    <div>{activity.description}</div>
                                    <div>{activity.city}, {activity.venue}</div>
                                </Item.Description>
                                <Item.Extra>
                                    <Button floated='right' content='View' color='blue'
                                        onClick={() => selectActivity(activity.id)}                                         
                                    />
                                    <Button name={activity.id} loading={target === activity.id && submitting} floated='right' content='Delete' color='red'
                                        onClick={(e) => deleteActivity(e,activity.id)}                                         
                                    />
                                    <Label basic content={activity.category} />
                                </Item.Extra>
                            </Item.Content>
                        </Item>
                    );
                })}
            </Item.Group>
        </Segment>
    );
}; 

export default observer(ActivityList);

ActivityForm.tsx:

import React, {useState, FormEvent, useContext} from 'react'
import { Segment, Form, Button } from 'semantic-ui-react';
import { IActivity } from '../../../app/models/activity';
import {v4 as uuid} from 'uuid';
import ActivityStore from '../../../app/stores/activityStore';
import { observer } from 'mobx-react-lite';

interface IProps {
    setEditMode: (editMode: Boolean) => void
    activity: IActivity,
    editActivity: (activity: IActivity) => void,
    submitting: boolean
}

const ActivityForm: React.FC<IProps> = 
    ({ setEditMode, activity: initalFormState, editActivity, submitting }) => {

    const activityStore = useContext(ActivityStore);
    const {createActivity} = activityStore;

    const initializeForm = () => {
        if (initalFormState) {
            return initalFormState;
        } else {
            return {
                id: '',
                title: '',
                description: '',
                category: '',
                date: '',
                city: '',
                venue: '',
            }
        }
    };

    const [activity, setActivity] = useState<IActivity>(initializeForm);

    const handleSubmit = () => {
        if (activity.id.length === 0) {
            let newActivity = {
                ...activity,
                id: uuid(),
            };
            createActivity(newActivity);
        } else {
            editActivity(activity);
        }
    };
    const handleInputChange = (event: FormEvent<HTMLInputElement | HTMLTextAreaElement>) => {
        const {name, value} = event.currentTarget;
        setActivity({...activity, [name]: value});
    }

    return (
        <Segment clearing>
            <Form onSubmit={handleSubmit}>
                <Form.Input 
                    onChange={handleInputChange}
                    placeholder='Title'
                    name='title'
                    value={activity.title}/>
                <Form.TextArea rows={2} 
                    onChange={handleInputChange}
                    placeholder='Description' 
                    name='description'
                    value={activity.description}/>
                <Form.Input 
                    onChange={handleInputChange}
                    placeholder='Category'
                    name='category'
                    value={activity.category}/>
                <Form.Input 
                    onChange={handleInputChange}
                    type='datetime-local' 
                    name='date'
                    placeholder='Date' 
                    value={activity.date}/>
                <Form.Input 
                    onChange={handleInputChange}
                    placeholder='City'
                    name='city'
                    value={activity.city}/>
                <Form.Input 
                    onChange={handleInputChange}
                    placeholder='Venue'
                    name='venue'
                    value={activity.venue}/>

                <Button loading={submitting} floated='right' positive type='submit' content='Submit' />
                <Button onClick={() => setEditMode(false)} floated='right' type='button' content='Cancel' />
            </Form>
        </Segment>
    )
}

export default observer(ActivityForm);

而且,我也面临一些控制台错误。如果您也可以指导我的新手自我,我将不胜感激enter image description here

enter image description here

PS:我是新来发问的人,如果我错过了东西,请放心,我将继续更新问题。一切都在github上tho

reactjs typescript .net-core react-hooks mobx
1个回答
0
投票

ActivityForm.tsx

<Button onClick={() => setEditMode(false)}将在您单击后立即运行。这将导致父级卸载此组件。

ActivityDashBoard.tsx {editMode && <ActivityForm

但是同时在[[ActivityForm.tsx中,您具有:<Form onSubmit={handleSubmit}>在卸载组件之前,该操作没有时间完成。

要解决此问题,您需要链接操作,如下所示:

const handleSubmit = () => { if (activity.id.length === 0) { let newActivity = { ...activity, id: uuid() }; createActivity(newActivity); } else { editActivity(activity); } setEditMode(false) // <-- we run it afterwards };


另一种解决方案是不使用{editMode && <ActivityForm,而是使用CSS / css-classs显示/隐藏。这样,您的组件将不会卸载,也不会出现此错误
© www.soinside.com 2019 - 2024. All rights reserved.