Get only undefined value from my global variable configured with Context - javascript

I have an React Native app with two pages. On the first page I have a picker from which I need the data from in the second page. I try to use Context for making sate globally available but I didn't get it to work till now because I only get undefined types at the position where I wanna insert the global state and not the value who was selected from the picker. I dont't get any errors but the field where the picker value should be represented is empty.
File from which I wanna get state from:
const FirstFile = () => {
const [selectedValueRound, setSelectedValueRound] = useState("10 rounds");
return (
<View>
<RoundContext.Provider
value={[selectedValueRound, setSelectedValueRound]}
>
<View>
<Picker
selectedValue={selectedValueRound}
onValueChange={(itemValue, itemIndex) =>
setSelectedValueRound(itemValue)
}
>
<Picker.Item label="1 round" value="0"></Picker.Item>
<Picker.Item label="2 rounds" value="1"></Picker.Item>
</Picker>
</View>
</RoundContext.Provider>
</View>
);
};
Context file:
export const RoundContext = createContext(false);
Navigation file where I wrap my context around
const Stack = createNativeStackNavigator();
const {selectedValueRound, setSelectedValueRound} = useContext(RoundContext);
const MyStack = () => {
return (
<RoundContext.Provider value={[selectedValueRound, setSelectedValueRound]}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="FirsFile" component={FirsFile} />
<Stack.Screen name="SecondFile" component={SecondFile} />
</Stack.Navigator>
</NavigationContainer>
</RoundContext.Provider>
);
};
File where I try to insert the global value:
const SecondFile = () => {
const [selectedValueRound, setSelectedValueRound] = useContext(RoundContext);
return (
<View>
<Text>{selectedValueRound}</Text>
</View>
);
};
export default SomeFile;

You also need to define context provider and wrap your app into it.
export const RoundContextProvider = ({children}) => {
const stateTuple = useState(false);
return <RoundContext.Provider value={stateTuple}>{children}</RoundContext.Provider>;
}
<RoundContextProvider>
<YourApp/>
</RoundContextProvider>
then you can use it as you described in the question: const [selectedValueRound, setSelectedValueRound] = useContext(RoundContext);

You must declare the state and the context provider in the top parent component. The children should only consume the values from the context.
The parent component
const MyStack = () => {
const [selectedValueRound, setSelectedValueRound] = useState("10 rounds");
const contextValue = useMemo(
() => [selectedValueRound, setSelectedValueRound],
[selectedValueRound]
);
return (
<RoundContext.Provider value={contextValue}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="FirsFile" component={FirsFile} />
<Stack.Screen name="SecondFile" component={SecondFile} />
</Stack.Navigator>
</NavigationContainer>
</RoundContext.Provider>
);
};
Note that I used useMemo to prevent passing a new array to the context when selectedValueRound did not change.
The children
const FirstFile = () => {
const [selectedValueRound, setSelectedValueRound] = useContext(RoundContext);
return (
<View>
<View>
<Picker
selectedValue={selectedValueRound}
onValueChange={itemValue => setSelectedValueRound(itemValue)}
>
<Picker.Item label="1 round" value="0"></Picker.Item>
<Picker.Item label="2 rounds" value="1"></Picker.Item>
</Picker>
</View>
</View>
);
};
const SecondFile = () => {
const [selectedValueRound] = useContext(RoundContext);
return (
<View>
<Text>{selectedValueRound}</Text>
</View>
);
};

Related

Context not working properly in React-Native

Here is my Context File:
import React from 'react';
import { createContext, useState } from 'react';
export const TabContext = createContext({
opened: false,
toggleOpened: () => {},
});
export const TabContextProvider = ({ children }) => {
const [opened, setOpened] = useState(false);
const toggleOpened = () => {
setOpened(!opened);
};
return (
<TabContext.Provider value={{ opened, toggleOpened }}>
{children}
</TabContext.Provider>
);
};
My Simplified App.js File: (Necessary files are imported)
const Tab = createBottomTabNavigator();
export default function App() {
const buttonCtx = useContext(TabContext);
return (
<>
<TabContextProvider>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name='Action'
component={ActionScreen}
options={{
tabBarButton: () => (
<ActionButton
opened={buttonCtx.opened}
toggleOpened={buttonCtx.toggleOpened}
/>
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
</TabContextProvider>
</>
);
}
And the Simplified ActionButton Component:
export default function ActionButton({ opened, toggleOpened }) {
return (
<View style={styles.container}>
<View style={styles.box}>
<TouchableWithoutFeedback
onPress={toggleOpened}
style={styles.actionButton}
>
/* With an Animated View inside */
</TouchableWithoutFeedback>
</View>
</View>
);
}
Basically, **toggleOpened **should switch the value of the variable **opened **between true and false. So the **AnimatedView **can work properly which solely depends on the value of opened.
Opened is readable in all of the components, no problem with that. But **toggleOpened **is not working at all. Any idea?
In order to use contexts properly, you need to have at least two components working together,one that renders the Provider and one descendant who then uses that context.
You are trying to provide and use the context at the same time,try to move the consumer component one position down to the hierarchy.
For example in your App.js you can create a consumer component to wrap your ActionButton,then pass the context to it as you did :
export default function App() {
return (
<>
<TabContextProvider>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="Action"
component={ActionScreen}
options={{
tabBarButton: () => <ActionButtonWrapper />
}}
/>
</Tab.Navigator>
</NavigationContainer>
</TabContextProvider>
</>
);
}
const ActionButtonWrapper = () => {
const { opened, toggleOpened } = useContext(TabContext);
return (
<>
<ActionButton
opened={opened}
toggleOpened={toggleOpened}
/>
</>
);
};
However,i would just use the context directly within ActionButton,after all,passing props down to children is what we want to avoid by using context,right?
I have created for you a snippet to see how we can properly use context

Change Textinput value when rendering Using .map() React native

i am using map function to render my component which have textInput . I want to change value of textInput using onchangeText function.
//main component
const [Value0, setValue0] = useState('');
const [Value1, setValue1] = useState('');
const [Value2, setValue2] = useState('');
..
const handleOnSubmit = () => { //fired when click from this
compnent button
console.log(Value0,"Value0");
console.log(Value1,"Value1");
}
//in my return i use :
{
data.map((item, id) => {
return (
<ViewDeatilCard1 key={id} /> //data having length 4
)
})
}
<ViewDeatilCard1 key={id} setChangeText={(value)=>{`'setValue${id}${(value)}'`}} /> //this
<ViewDeatilCard1 key={id} setChangeText={()=>{`'setValue${id}'`}} /> //this
<ViewDeatilCard1 key={id} setChangeText={`'setValue${id}'`} /> //this
// none of this work
// in my component i use
export const ViewDeatilCard1 = ({
setChangeText
}) => {
console.log(setChangeText,"setChangeText");
return (
<View style={styles.container}>
<View style={styles.body}>
<FormInput
style={styles.bodyText}
labelText="Enter pick up loaction"
iconName="null"
onChangeText={setChangeText}
/>
</View>
</View>
)}
how can i change my value using this approach

undefined function when passing navigation AND props to a react native component (basic disconnect button)

I'm a complete newbie and I'm trying to have a stack navigator only accessible if the user is logged, which works, but I can't manage to have a correctly working disconnect button.
I'm using a simple bool so far to grant access. The function used to disconnect, passed as a prop, is not found when I'm using the disconnect button.
App/Login screen :
const Stack = createStackNavigator();
export default function App() {
const [userIsLogged, setUserLog] = useState(false);
if (!userIsLogged) {
return <LoginScreen setUserLog={setUserLog}/>;
} else {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home">
{props => <Home {...props} setUserLog={setUserLog}/>}
</Stack.Screen>
<Stack.Screen name="Rooms" component={Rooms}/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
Where the disconnect button is called:
const Home = ({navigation}, props) => {
return (
<View style={styles.container}>
<Text>Home</Text>
<TouchableOpacity onPress={() => props.setUserLog(false)}>
<Text>DISCONNECT</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('Rooms')}>
<Text>ACCESS TO ROOMS</Text>
</TouchableOpacity>
<StatusBar style="auto" />
</View>
);
};
(Code has been simplified heavily to only highlight my issue)
Change
const Home = ({navigation}, props) => {
to
const Home = (props) => {
const {navigation} = props;
return /* REST OF CODE */
({navigation}, props) doesn't split the props into two groups, it defines two arguments to your function. Which means props will always be undefined since you never pass a second argument in this case.

How to pass a function to update state in Navigation Screen Component - React Native

I'm fairly new to React Native and I have created a Drawer Navigator in my App.js file.
One of my navigation components is named LifeScreen.
I am trying to pass a function to update a state (setSavedQuotes) to LifeScreen so that in LifeScreen I can update the value of the state (savedQuotes). This is straightforward in React but I can't seem to be able to do the same in React Native.
App.js (Navigator)
const App = () => {
const [savedQuotes, setSavedQuotes] = useState([])
return (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen
name="Home"
component={HomeScreen}
initialParams={{ sam: savedQuotes }}
/>
<Drawer.Screen
name="Life"
component={props => {
return <LifeScreen props={props} setSavedQuotes={setSavedQuotes} />
}}
/>
<Drawer.Screen name="Work" component={WorkScreen} />
<Drawer.Screen name="Saved" component={SavedScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
LifeScreen.js
const LifeScreen = ({ route, setSavedQuotes }) => {
const [quote, setQuote] = useState('')
const lifeWisdom = [...] // array of quotes
const getQuote = () => {... } // get Random Quote
const saveQuote = () => {
// console.log('life:', route.params)
console.log("function: ", setSavedQuotes); // returns undefined
}
return (
<View style={styles.mainContainer}>
<TouchableOpacity onPress={getQuote} style={styles.quoteContainer}>
<Text style={styles.quote}>{quote}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={saveQuote} style={styles.imgContainer}>
<AntDesign name="hearto" size={40} color="black" />
</TouchableOpacity>
</View>
);
};
Whenever I console.log(setSavedQuotes), I get undefined.
I can pass props and the state value without any problem, as I did with HomeSCreen in App.js.
I tried the following: regarding props, initialParams, react-navigation and route. I can manage to pass the state with all of them, but not the function to update the state.

How to make the react native switch to true when list of toggle button is rendered through a map?

Problem:
I am rendering a set of toggle buttons through a map. Now I want to make it true or false each when the user is changing the value of each toggle. This is how I have created the toggle component.
const AnswerToggle = (props) => {
const {styles, name} = props;
return (
<View style={styles.answerContentContainer}>
<View style={styles.answerTextContainer}>
<AppText styles={styles.answerText}>{name}</AppText>
</View>
<View style={styles.container}>
<Switch
trackColor={{false: '#dddddd', true: '#c1d6ee'}}
thumbColor={{false: '#ffffff', true: '#007aff'}}
ios_backgroundColor="#dddddd"
// ref={name}
onValueChange={
(value) => {
// ref[name].value = true;
}
// console.log(
// '>>>>>> value',
// this[`${name}`].value,
// )
}
style={styles.toggle}
/>
</View>
</View>
);
};
And I am loading it through map like this.
return answers.map((answer, i) => {
return (
<AnswerToggle
key={i}
styles={styles}
name={name}
/>
);
});
I try to do it by giving reference to the Switch component. Then It says you cannot use ref without forwardRef so then I put it to the AnswerToggle component but it still giving me the error can some help me to solve this issue?. I tried lot to find out a solution to this problem. But I was unable to do so
Define the onChange handler in the parent component and pass it in as a prop. When the switch is flipped update the state in the parent accordingly and pass the new value to AnswerToggle as a prop.
// pseudo code
const [switchValues, setSwitchValues] = useState([]);
const onChange = (index, value) => setSwitchValues( ... );
answers.map((a, i) => <AnswerToggle value={switchValues[i]} onChange={newValue => onChange(i, newValue) />
This will work just fine:
const AnswerToggle = (props) => {
const {styles, name} = props;
const [toggleStatus, setToggle] = React.useState(false)
const onChange = () => setToggle(status => !status)
return (
<View style={styles.answerContentContainer}>
<View style={styles.answerTextContainer}>
<AppText styles={styles.answerText}>{name}</AppText>
</View>
<View style={styles.container}>
<Switch
trackColor={{false: '#dddddd', true: '#c1d6ee'}}
thumbColor={{false: '#ffffff', true: '#007aff'}}
ios_backgroundColor="#dddddd"
onChange={onChange}
value={toggleStatus}
style={styles.toggle}
/>
</View>
</View>
);
};
EDIT:
If you need to set the statuses of toggles into the parent component, this is my solution for you:
const AnswerToggle = (props) => {
const {styles, name, onChange, value} = props;
return (
<View style={styles.answerContentContainer}>
<View style={styles.answerTextContainer}>
<AppText styles={styles.answerText}>{name}</AppText>
</View>
<View style={styles.container}>
<Switch
trackColor={{false: '#dddddd', true: '#c1d6ee'}}
thumbColor={{false: '#ffffff', true: '#007aff'}}
ios_backgroundColor="#dddddd"
onChange={() => onChange(name)}
value={value}
style={styles.toggle}
/>
</View>
</View>
);
};
const Parent = props => {
// ... other code
// set all toggles to false
const [toggleStatuses, setToggle] = React.useState(
answers.reduce((toggles,answer) => {
toggles[answer.name] = false
return toggles
},{})
);
const onChange = name => setToggle(state => ({
...state,
[name]: !state[name],
}));
return answers.map((answer, i) => {
return (
<AnswerToggle
value={toggleStatuses[answer.name]}
onChange={onChange}
key={i}
styles={styles}
name={answer.name}
/>
);
});
}

Categories

Resources