如何合并...对象并在setState(react-hook)中返回函数调用的值?

时间:2019-06-27 16:31:08

标签: reactjs react-hooks

无法合并...函数返回的状态和结果。

我试图将类组件更改为功能组件。

所以我更新了react并使用了钩子。

首先,我想更改类的状态,将setState更改为钩子的状态。

但是钩子的setState替换对象不能像类的setState那样合并。

下面是原始代码

import React from 'react'
import produce from 'immer'
import {
  getUserFromCookie,
  login,
  logout,
  profile,
  updateProfile
} from '../api'

const userInfo = getUserFromCookie()
const UserContext = React.createContext({
  ...userInfo
})

export const withUserContext = WrappedComponent => {
  return class ProviderComponent extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
        ...userInfo,
        consentNeeded: false,
        updateConsent: async ({ pi, news, seen }) => {
          await updateProfile({ pi, news, seen })
          this.setState({
            consentNeeded: false
          })
        },
        profile: async () => {
          const userProfile = await profile()
          if (userProfile.seen_consent_modal === false) {
            this.setState({
              consentNeeded: true
            })
          }
        },
        login: async ({ userId, password }) => {
          const user = await login({ userId, password })
          this.setState(
            produce(draft => {
              return user
            })
          )
        },
        logout: async () => {
          await logout()
        }
      }
    }

    render() {
      return (
        <UserContext.Provider value={this.state}>
          <WrappedComponent {...this.props} />
        </UserContext.Provider>
      )
    }
  }
}

export default UserContext

这是我工作的功能组件。

import React, { useState } from 'react'
import produce from 'immer'
import {
  getUserFromCookie,
  login,
  logout,
  profile,
  updateProfile
} from '../api'

const userInfo = getUserFromCookie()
const UserContext = React.createContext({
  ...userInfo
})

export const withUserContext = WrappedComponent => {
  return function provideComponent() {

    const [state, setState] = useState({
      ...userInfo,
      consentNeeded: false,
      updateConsent: async ({ pi, news, seen }) => {
        console.error('updateConsent!!')
        await updateProfile({ pi, news, seen })
        setState({
          consentNeeded: false
        })
      },
      profile: async () => {
        console.error('profile!!')
        const userProfile = await profile()
        if (userProfile.seen_consent_modal === false) {
          setState({
            consentNeeded: true
          })
        }
      },
      login: async ({ userId, password }) => {
        const user = await login({ userId, password })

        setState(
          produce(() => user)
        )
      },
      logout: async () => {
        await logout()
      }
    })

    return (
      <UserContext.Provider value={state}>
        <WrappedComponent {...props} />
      </UserContext.Provider>
    )
  }
}

export default UserContext

下划线警告。我认为语法不正确

1 个答案:

答案 0 :(得分:0)

编辑:

我意识到了问题所在。我做了一个codesandbox,一切正常(除了您未提供的功能)。

1。 HOC应该用于Contex.Consumer而不是Context.Provider

在您的代码中,您正在为HOC创建一个Context.Provider,但正确的方法应该是为Contex.Consumer创建。

要使用上下文,您需要

<Contex.Provider>
    ...
    <AnyThingYouWant>
        <Context.Consumer>
        </Context.Consumer>
    </AnyThingYouWant>
</Contex.Provider>

如果您想将HOC用作Contex.Provider,则只需使用children并将其包装在组件中

例如

const UserContext = React.createContext('my context')

const UserProvider = (props) => {

    const value = useState('someState')

    return (
        <UserContext.Provider value={value}>
            {children}
        </UserContext.Provider>
    )

}

2。如果您使用的是功能组件,则不再需要HOC

React Hooks引入了useContext

现在,您唯一需要渲染Context.Provider并像const {...contextValue} = useContext(MyContext)这样使用它。

例如

const { updateConsent, profile, login, logout, ...otherStuff } = useContex(UserContext)

3。在Context.Consumer内,您需要传递一个呈现WrappedComponent的函数

HOC制作Context.Consumer时,您需要具有一个渲染WrappedComponent并从使用者那里获取props的功能。

例如

const withUserContext = WrappedComponent => {
  return function UserContextHoc(props) {
    return (
      <UserContext.Consumer>
        // function that render the `WrappedComponent`
        {consumerProps => <WrappedComponent {...props} {...consumerProps} />}
      </UserContext.Consumer>
    );
  };
};

如果您这样做,那是错误的

<UserContext.Consumer>
    // THIS IS WRONG AND WILL THROW AN ERROR
    <WrappedComponent {...props} />
</UserContext.Consumer>

如果您查看codesandbox,将发现它没有错误,并且在console内的MyComponent中,它显示了UserContext中的所有内容

希望现在一切都变得更清楚了。


旧:

您的函数应该不在useState初始值的范围内,以便能够调用setState

// state has multiple key value
 const [state, setState] = useState({
      ...userInfo,
      consentNeeded: false,      
    })
const updateConsent = async ({ pi, news, seen }) => {
        await updateProfile({ pi, news, seen })
        setState({
          consentNeeded: false
        })
      }

const profile = async () => {
        const userProfile = await profile()
        if (userProfile.seen_consent_modal === false) {

          // setState(prevState => {
          //   return {...prevState, {consentNeeded: true}};
          // });
          setState({
            consentNeeded: true
          })
        }
      }

const login = async ({ userId, password }) => {
        const user = await login({ userId, password })
        // code below change it as produce's result. 
        // not merging of exist states

        // setState(
        //   produce(() => {
        //     return user
        //   })
        // )

       // what I've tried.. but warning underline..
        setState(prevState => {...prevState, produce(() => user)})
}

const logout = async () => {
    await logout()
}

    return (
      <UserContext.Provider value={{
        ...state,
        updateConsent,
        profile,
        login,
        logout,
      }>
        <WrappedComponent {...props} />
      </UserContext.Provider>
    )