I'm trying to access useTheme() directly from the styles
But so far my solution doesn't seem to work.
I'm not getting in error back.
Is there a way to do what I'm trying to do?
import { StyleSheet } from "react-native";
import { useTheme } from '#react-navigation/native'
export default function () {
const { colors } = useTheme();
const styles = GlobalStyles({ colors: colors })
return styles
}
const GlobalStyles = (props) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: props.colors.backgroundColor,
},
})
Accessing style in component
import React from "react";
import GlobalStyles from "../globalStyles.js"
class Inventory extends React.Component {
render() {
return (
<View style={globalStyles.container}>
)
}
You have a couple of issues: you can only use a hook within a hook or a function component. So you could convert your global stylesheet into a hook:
import { StyleSheet } from "react-native";
import { useTheme } from '#react-navigation/native'
const getGlobalStyles = (props) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: props.colors.backgroundColor,
},
});
function useGlobalStyles() {
const { colors } = useTheme();
// We only want to recompute the stylesheet on changes in color.
const styles = React.useMemo(() => getGlobalStyles({ colors }), [colors]);
return styles;
}
export default useGlobalStyles;
Then you can use the hook by converting your Inventory class component into a function component and using the new useGlobalStyles hook.
import React from "react";
import useGlobalStyles from "../globalStyles.js"
const Inventory = () => {
const globalStyles = useGlobalStyles();
return (
<View style={globalStyles.container}>
)
}
Related
I want to use hook to get and set the value of my component WITHOUT PROPS. I import the component and it lists the data returned from the request, ok, but when I use the hook to see the data it returns empty, as if it were another instance.
Initially I used the state of the parent component, but when I needed to change some value of my component, everything would re-render as it affected the state of the parent component, so I want to isolate the state in the child component and use it freely as a hook elsewhere .
How I would like to use:
import MyComp, { useMyHook } from '../../../components/MyComp';
const OtherComp = () => {
const { data } = useMyHook();
return(
<div>
<button type="button" onClick={() => console.log(data)}>
click test
</button>
<MyComp />
</div>
);
};
export default OtherComp;
Component render
Example1
Example2
But the click button log: [ ]
Without using external components/libs like redux and etc.
My custom hook:
src/useMyHook.ts
import { useState } from 'react';
export const useMyHook = () => {
const [data, setData] = useState<any[]>([]);
const addItem = (item: unknown) => {
setData([...date, item]);
};
return { data, setData, addItem};
};
export default useMyHook;
My main component:
src/MyComp.tsx
import {useEffect} from 'react';
import useMyHook from './useMyHook';
const MyComp = () => {
const { data, setData } = useMyHook();
const req = async() => {
const {values} = await anyRequest(); // to do any request
setData(values);
};
useEffect(() => { req() },[]);
return(
<div>
{data.map((item) => <p>{item.name}</p>)}
</div>
);
};
export { useMyHook };
export default MyComp;
src/index.tsx
import MyComp, { useMyHook } from './MyComp';
export default MyComp;
export { useMyHook };
To demonstrate my comment:
import React,{useState, createContext} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
import useMyHook from './useMyHook'
import Example1 from './Example1'
import Example2 from './Example2'
import OtherComp from './OtherComp'
import {Data, DataSetter} from './types'
type DContext = {
data:Data,
setData:DataSetter
}
export const DataContext = createContext({
data:[],
setData:()=>{}
} as DContext)
export default function App() {
const [data, setData] = useState<Data>([])
return (
<View style={styles.container}>
<DataContext.Provider value={{data,setData}}>
<Example1/>
<Example2/>
<OtherComp/>
</DataContext.Provider>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
import { useContext, useCallback } from 'react';
import { DataContext } from './App';
export const useMyHook = () => {
const { data, setData } = useContext(DataContext);
// wrapped in useCallback to prevent function from
// being recreated
const addItem = useCallback((item: unknown) => {
setData(prev=>[...prev, item]);
},[]);
return { data, setData, addItem };
};
export default useMyHook;
useMyHook usage:
import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import useMyHook from './useMyHook';
export default function Example1() {
const { data, addItem } = useMyHook();
return (
<View style={{ width: '100%' }}>
<Button
title="Add Item From Example 1"
onPress={() => {
addItem(Math.floor(Math.random() * 25) + ' From Example1');
}}
/>
</View>
);
}
A demo
I got the error while evaluating a const object, here's a minimal working example:
import React from "react";
import { View, StyleSheet } from "react-native";
const App = () => {
return (
<View style={styles.container}></View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor:colors.bg,
},
});
const colors = {
bg:'blue',
}
export default App;
Error message: TypeError: undefined is not an object (evaluating 'colors.bg')
What's going on here?
You have to define colors const before the styles.
Try This
import React from 'react';
import {View, StyleSheet} from 'react-native';
const App = () => {
return <View style={styles.container}></View>;
};
const colors = {
bg: 'blue',
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.bg,
},
});
export default App;
This my main class
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import styles from './FoodStyles';
class Food extends Component {
render () {
return (
<div>
<h2 className="header">Food</h2>
</div>
)
}
}
export default withStyles(styles) (Food);
And this is my style class called FoodStyles.js
const styles = theme => ({
header: {
backgroundColor: 'red'
},
});
export default styles;
They both are in the same folder but still styles cannot be accessed
This could be the solution to your problem: (You need destructuring as done in line 7 for your styles to be used in your file.) With React, which fully embraces the ES6 syntax, destructuring adds a slew of benefits to improving your code.
Food.js:
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import styles from './styles';
class Food extends Component {
render () {
const { classes } = this.props;
return (
<div>
<h2 className={classes.header}>Food</h2>
</div>
)
}
}
export default withStyles(styles)(Food);
styles.js:
const styles = theme => ({
header: {
backgroundColor: 'red'
},
});
export default styles;
Result:
Reasons to destructure:
1. Improves readability:
This is a huge upside in React when you’re passing down props. Once you take the time to destructure your props, you can get rid of props / this.props in front of each prop.
2. Shorter lines of code:
Insead of:
var object = { one: 1, two: 2, three: 3 }
var one = object.one;
var two = object.two;
var three = object.three
console.log(one, two, three) // prints 1, 2, 3
We can write:
let object = { one: 1, two: 2, three: 3 }
let { one, two, three } = object;
console.log(one, two, three) // prints 1, 2, 3
3. Syntactic sugar:
It makes code look nicer, more succinct, and like someone who knows what they’re doing wrote it.
I would just use makeStyles instead of withStyles.
App.jsx
import Food from "./Food";
const App = () => {
return (
<div className="App">
<Food />
</div>
);
};
export default App;
Food.jsx
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import styles from "./FoodStyles";
const useStyles = makeStyles(styles);
const Food = () => {
const classes = useStyles();
return (
<div>
<h2 className={classes.header}>Food</h2>
</div>
);
};
export default Food;
FoodStyles.js
const styles = (theme) => ({
header: {
backgroundColor: "red"
}
});
export default styles;
TypeError: undefined is not an object (evaluating 'this.props.all_subjects.current').
Index.js
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
App.js
import 'react-native-gesture-handler';
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { StyleSheet } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import subjectsReducer from './reducers/subjectsReducer';
import Home from "./screens/home";
import Subjects from "./screens/subjects";
const store = createStore(subjectsReducer);
const Stack = createStackNavigator();
class App extends Component {
render() {
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
/>
<Stack.Screen
name="Subjects"
component={Subjects}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
}
}
export default App;
subjectsReducers.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
current: [],
all_subjects: [
'Literature',
'Speech',
'Writing',
'Algebra',
'Geometry',
'Statistics',
'Chemisrty',
'Biology',
'Physics',
'Economics',
'Geography',
'History',
],
};
const subjectsReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SELECT_SUBJECT':
// copy the state
const { current, all_subjects,} = state;
//remove a subject from the all_subjects array
const addedSubject = all_subjects.splice(action.payload, 1);
// put subject in current array
current.push(addedSubject);
// update the redux state to reflect the change
const newState = { current, all_subjects };
//return new state
return newState;
default:
return state
}
};
export default combineReducers({
subjects: subjectsReducer
});
subjectsActions.js
export const addSubject = subjectsIndex => (
{
type: 'SELECT_SUBJECT',
payload: subjectsIndex,
}
);
Subjects.js
import React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Button } from 'react-native';
class Subjects extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Select Subjects Below!</Text>
{this.props.subjects.all_subjects.map((subject, index) => (
<Button
key={ subject }
title={ `Add ${ subject }` }
onPress={() =>
this.props.addSubject(index)
}
/>
))}
<Button
title="Back to home"
onPress={() =>
this.props.navigation.navigate('Home')
}
/>
</View>
);
}
}
// const mapStateToProps = (state) => {
// const { subjects } = state
// return { subjects }
// };
const mapStateToProps = state => ({
subjects: state.subjects
});
export default connect(mapStateToProps)(Subjects);
Home.js
import React from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Button } from 'react-native';
class Home extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>You have { this.props.all_subjects.current.length } subjects.</Text>
<Button
title="Select more subjects"
onPress={() =>
this.props.navigation.navigate('Subjects')
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text:{
color: '#888',
fontSize: 18
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
submitButton: {
backgroundColor: '#7a42f4',
padding: 10,
margin: 15,
height: 40,
},
submitButtonText:{
color: 'white'
}
});
// const mapStateToProps = (state) => {
// const { subjects } = state
// return { subjects }
// };
const mapStateToProps = state => ({
subjects: state.subjects
});
export default connect(mapStateToProps, null)(Home);
I am new to react native. I have searched everywhere for a solution for this issue, but I can’t seem to get anywhere. I have used redux in standard react, but I don’t know why this isn’t working. Any thoughts or suggestions would be greatly appreciated.
I want to position 2 components underneath each other. My component looks like this :
import React from 'react';
import {connect} from 'react-redux';
import {Text, StyleSheet, View, ListView, Switch, TextInput} from 'react-native';
import {List, ListItem} from 'native-base';
import SearchBar from '../shared/search-bar';
const mapStateToProps = (state) => {
return {
movies: state.movies.movies
};
};
const mapDispatchToProps = (dispatch) => {
return {};
};
const Component = ({movies}) => {
return (
<View style={styles.container}>
<SearchBar style={styles.searchBarContainer}/>
<List
style={styles.list}
dataArray={movies}
renderRow={movie => <ListItem><Text>{movie.title}</Text></ListItem>}
/>
</View>
);
};
let styles = StyleSheet.create({
container: {
flexDirection: 'column',
flex: 1,
},
searchBarContainer: {
flex: 0
},
list: {
flex: 1
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Component);
For some reason, when they are rendered, the list is just rendered over the searchbar. Why does the flexDirection:'column' not position them underneath each other?
I feel like I'm missing something about the flexbox layout.
in your style.container, remove the flex entry but keep the flexDirection.