如何从另一个组件单击按钮来更新一个类组件中的状态?

时间:2020-04-19 16:53:10

标签: javascript reactjs react-native react-navigation react-state

我对React Native还是比较陌生,并且想建立一个非常简单的身份验证流程。基本逻辑在起作用,我无法处理的是需要更改顶级App.js中状态的“登录”按钮事件:

  1. 我有一个顶层App.js,它呈现了一个具有3个可能屏幕的简单Stack Navigator:SplashScreen(直到isLoading状态更改为false),LoginScreen(如果isLoggedIn状态为false)和LaunchScreen(如果isLoggedin状态为真)

  2. 根据docs,我运行加载函数,该函数从App.js的componentDidMount()函数中的远程服务器获取用户身份验证数据。无论返回什么结果,它都会将isLoading状态设置为false并将isLoggedIn设置为false设置为true。假设为了简单起见,现在返回1并完成了加载。 isLoggedIn暂时保持为假,因此用户进入登录屏幕。

App.js

import SplashScreen, {myLoadingFunction} from './SplashScreen.js'
// Other imports

const Stack = createStackNavigator()

export default class App extends React.Component {

  constructor(props) {
    super(props);      
    this.state = {
      isLoggedIn: false,
      isLoading: true
    }      
  }

  componentDidMount() {
    if (myLoadingFunction () == 1) {
      this.setLoadingFinished()
    }
  }

   setLoggedIn = () => (   
    this.setState({ 
      isLoggedIn: true,
    })    
   )

   setLoadingFinished = () => (   
    this.setState({ 
      isLoading: false
    })    
   )

  render() {
    // Show SplashScreen while verifying user token
    if (this.state.isLoading) return <SplashScreen />

    const { contextState } = this.context

    return (
      <MyProvider>
        <NavigationContainer>
              <Stack.Navigator>

                {contextState.isLoggedIn == true ? (
                  // User is signed in
                  <Stack.Screen
                    name="LaunchScreen"
                    component={LaunchScreen}
                  />
                ) : (

                   // No token found, user isn't signed in
                  <Stack.Screen name="LoginScreen" component={LoginScreen} />
                )}

              </Stack.Navigator>
        </NavigationContainer>
      </MyProvider>
    );
  }

}
App.contextType = MyContext

SplashScreen.js

export const myLoadingFunction = () => {
    // fetching some data from server
    console.log("Splash Screen loading/fetching data...")
    return 1
}

export default class SplashScreen extends React.Component {

    render (){
        return (
            <View>
                <Text>Application is being loaded...</Text>
            </View>
        )
    }    
}

直到确定可以通过安装App组件后,我将使用一个简单的逻辑(myLoadingFunction)来验证用户是否具有有效的令牌,然后返回一个值。当前,它将isLoading状态更改为false,不更改isLoggedIn状态,因此用户位于登录屏幕上。

LoginScreen.js

export const myFetchUserToken = () => {
    console.log("Login Screen fetching data...")
    return 1
}

export default class LoginScreen extends React.Component {

    render (){
        return (
            <View>
            <Text>You are currently logged out</Text>
            <Button title="Log In" onPress={()=>{}}/>
            </View>
        )
    }    
}
LoginScreen.contextType = MyContext

单击此按钮时,我只想在App中将isLoggedIn状态更改为true(为了简单起见,现在跳过auth逻辑)。这将触发App中的渲染功能,并向用户显示启动屏幕。我该如何实现?

我尝试过的事情:

  • 将setLoggedIn()函数作为道具传递给SplashScreen,但是Stack.Screen不接受道具
  • 导出setLoggedin()函数并将其导入到SplashScreen中,但是很明显,由于没有setState()函数,在App Component外部的setLoggedIn()失败
  • 使用Context API从isLoggedin创建全局状态-进一步了解:我可以更新状态,但App.js不会重新呈现

MyContext.js

const MyContext = React.createContext({
    name: "defName",
    age: 0,
    setUser: (name) => {},

})


class MyProvider extends React.Component {
    // Context state
    state = {
      name: "stateName",
      age: 20,
      isLoggedIn:false
    }

    setUser = (name) => {
      this.setState({
         name: "changedName"
      })
    }

    setLogInTrue = () => {
      this.setState({
        isLoggedIn: true
      })
    }

    render() {  
      const {children} = this.props
      return (
        <MyContext.Provider
          value={{
            contextState: this.state,
            setUser: this.setUser,
            setLogInTrue: this.setLogInTrue 
          }}
        >
        {children}
        </MyContext.Provider>
      )
    }
  }

export default MyContext
export { MyProvider }

更新

有了Expo,我有了AppEntry.js,可以在其中将应用程序包装到MyContext中:

AppEntry.js

ReactDOM.render(
    (    
    <MyProvider>
      <App />
    </MyProvider>
    ),
    document.getElementById('root'),
  );

这样,我将条件this.state.isLoggedIn == true更改为contextState.isLoggedIn == true,现在可以使用了。还向MyContext添加了setLogInTrue()函数(上面已更新),可以在LoginScreen Button中调用它:

更新了LoginScreen.js

export default class LoginScreen extends React.Component {

    render (){
        const { contextState, setLogInTrue } = this.context
        console.log(contextState)
        return (
            <View>
            <Text>You are currently logged out</Text>
            <Button title="Launch Application" onPress={setLogInTrue}/>
            </View>
        )
    }    
}
LoginScreen.contextType = MyContext

如果我console.log contextState,它现在将isLoggedIn状态更改为true。但是App.js不会重新呈现(“登录屏幕”会根据日志重新呈现)。为什么?

更新2

好的,只有一个拼写错误(在上下文状态为loginIn,我更改了isLoggedIn的状态,而不是loggedIn)

如果有人需要,我会将整个代码留在这里。

1 个答案:

答案 0 :(得分:0)

尝试在app.js中使用上下文api并在其中设置函数和状态。

示例:- 1:- 写这些行 const defaultValue =更改状态的函数 const MyContext = React.createContext(defaultValue); 2:- 将app.js中的所有组件包装在一起 ...所有组件