状态对象发生变化时更新上下文

时间:2019-09-27 01:10:52

标签: javascript node.js reactjs typescript state

我有一个PageContext,它以数组的形式保存着User对象的状态。每个User对象都包含一个ScheduledPost对象,当用户决定添加新帖子时,该对象会发生变异。我不知道如何在PageContext发生时触发更新(我想避免进行forceUpdate()通话)。我需要以某种方式得到通知,以便重新呈现帖子,维护计时器等。

请查看代码:

class User {
    name: string;
    createTime: number;
    scheduledPosts: ScheduledPost[] = [];

    /*
     * Creates a new scheduled post
     */
    public createScheduledPost(title : string, content : string, date : number): void {
        this.scheduledPosts.push(Object.assign(new ScheduledPost(), {
            title,
            content,
            date
        }));
    }
}

class ScheduledPost {
    title: string;
    content: string;
    date: number;

    public load(): void {
        // Create timers etc.
    }

    public publish(): void {
        // Publish post
    }
}

// PageContext/index.tsx
export default React.createContext({
    users: [],
    editingUser: null,
    setEditingUser: (user: User) => {}
});

// PageContextProvider.tsx
const PageContextProvider: React.FC = props => {
    const [users, setUsers] = useState<User[]>([]);
    const [editingUser, setEditingUser] = useState<User>(null);

    // Load users
    useEffect(() => {
        db.getUsers()
            .then(result => setUsers(result));
    }, []);

    return (
        <PageContext.Provider value={{
            users,
            editingUser,
            setEditingUser
        }}>
            {props.children}
        </PageContext.Provider>
    );
};

我想实现的是,使用useContext钩子来使用我的提供程序时:

const ctx = useContext(PageContext);

我想从这样的任何组件创建日程表:

// Schedule post (1 hour)
ctx.editingUser.createScheduledPost("My post title", "The post content", (new Date).getTime() + 1 * 60 * 60);

但是,这不会起作用,因为React不知道User属性刚刚发生了变异。


问题:

  • 如何使React收到任何User对象实例中的更改的通知?正确解决该问题的方法是什么(不包括forceUpdate)?
  • 我做对了吗?我是React的新手,我觉得我在这里使用的结构笨拙而且不正确。

2 个答案:

答案 0 :(得分:3)

在哪里突变用户?如果您将它们存储在显示的状态,则应检测到更改。但是,如果您使用User类中内置的方法让它们自己directly update,那么React将不会使用它们。您需要在您的状态下更新整个users数组,以确保React可以响应更改。

很难给出一个具体的示例,而又不知道您当前在何处/如何更新用户,但是一个广义的变异可能会发生这样的事情(如果需要,您仍然可以使用类方法):

const newUsers = Array.from(users); // Deep copy users array to prevent direct state mutation.
newUsers[someIndex].createScheduledPost(myTitle, myContent, myDate);
setUsers(newUsers); // Calling the setX function tied to your useState call will automatically trigger updates/re-renders for all (unless otherwise specified) components/operations that depend on it

答案 1 :(得分:1)

在React中,重新渲染是由在组件内调用setState(或通过使用hooks,但要指出的是您需要调用特定方法)或更改组件props引起的。这意味着您手动更改状态永远不会导致重新渲染-即使您使用简单的组件并称为

this.state.something = somethingElse;

不会发生重新渲染。同一件事适用于上下文。

对于您的情况,这意味着您不应更改editingUser状态,而应使用已更改的用户状态调用setEditingUser,例如:

const user = { ...ctx.editingUser };
user.createScheduledPost("My post title", "The post content", (new Date).getTime() + 1 * 60 * 60);
ctx.setEditingUser(user);

我不确定您的内部结构,但是如果同一用户也位于users数组中,那么您将需要通过调用您维护的setUsers方法来更新那部分状态整个数组,并且只更新更改数据的单个用户-如果是这种情况,那么我会考虑重组应用程序,因为对于这样简单的状态更改,它已经变得很复杂。您还应该考虑使用reduxmobx或其他状态管理库,而不是使用响应上下文(我的个人建议)。

编辑

请仔细看看:

  

在典型的React应用程序中,数据是自上而下传递的(对于   儿童)通过道具,但这对于某些类型的   许多人需要的道具(例如语言环境偏好设置,UI主题)   应用程序中的组件。上下文提供了一种共享方式   无需显式传递组件之间类似这些值   穿过树的每一层的道具。

如您所见,React团队建议对许多组件中所需的某些全局首选项使用上下文。使用上下文的主要问题(我认为)是,您没有编写 natural 反应组件-它们不会通过prop而是从上下文api本身接收相关数据。这意味着,如果不集成应用程序的上下文部分,就无法重用组件。

例如,虽然redux具有类似的将状态保持在一个位置的概念,但它仍通过props将该状态(及其更改)传播到组件,使您的组件不受redux,上下文或其他任何东西的依赖。

您可以坚持对上下文做出反应,并使整个应用程序都可以使用它,但是我只是说这样做不是最佳实践。