Rendering text on a page from the redux-store - reactjs

I have a text input in React-Native component page and I send the input text to the store.
I am trying to render the input text on the page from the redux store but it will not display.
Below are my component and my redux store. Am I not calling the value correctly in my component with this.props.email?
COMPONENT
import React, { Component} from 'react';
import { StyleSheet, View, Button, Text, StatusBar, TextInput, TouchableOpacity, Image } from 'react-native';
import { connect } from 'react-redux';
class Counter extends Component {
email(value) {
this.props.email(value);
}
toggleCounterHandler() {}
render() {
return (
<View>
<View ><Text style={styles.welcometext}>{this.props.email}</Text></View>
<View>
<TextInput
style={styles.input}
onChangeText = {(value) => this.props.email(value)}
value={this.props.value}
/>
</View>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
value: state.value
};
}
const mapDispatchToProps = dispatch => {
return {
email: (value) => dispatch({
type: 'email',
payload: value
})
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
REDUX-STORE
import React from 'react';
import { createStore } from 'redux';
const counterReducer = (state = { email:'email' }, action) => {
if (action.type === 'email') {
return {
email: action.payload,
};
}
return state;
};
const store = createStore(counterReducer);
export default store;

Related

Redux - mapStateToProps not working (React-Native)

I am learning Redux and can't seem to get state to display in my home page. I get the error: 'undefined is not an object, evaluating this.props.titles.allTitles. The error is located in Home created by connect function' Here is the code, let me know if you need any other files. Thank you. I am adding more text to comply with stack overflow, thank you for your help.
home:
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import { connect } from 'react-redux'
class Home extends React.Component {
render() {
return (
<View>
<Text>Redux Test</Text>
<Button
title='+ new list'
onPress={() =>
this.props.navigation.navigate('New List')
}
/>
<Text>{this.props.titles.allTitles.length}</Text>
</View>
)
}
}
const mapStateToProps = (state) => {
const { titles } = state
return { titles }
};
export default connect(mapStateToProps) (Home);
```
reducer:
```
import { combineReducers } from 'redux';
const INITIAL_STATE = {
allTitles: []
};
const tagReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'NEW_LIST':
return {
...state,
allTitles: [...state.allTitles, action.payload.title]
}
default:
return state;
}
};
const reducers = combineReducers({
tagReducer
})
export default reducers;
```
import React from 'react';
import { StyleSheet, Text, View, Button, TextInput } from 'react-native';
import { connect } from 'react-redux';
import { newList } from '../store/tagActions';
class List extends React.Component {
constructor(props){
super(props);
this.state = {
title: ''
}
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.title}
placeholder='add Title..'
onChangeText={text => this.setState( {title: text} ) }
/>
<Button
title='done'
onPress={() => {
this.props.newList(this.state.title)
}
}
/>
<Text>{this.state.title}</Text>
</View>
)
}
}
const mapStateToProps = (state) => {
const { allTitles } = state
return { allTitles }
};
export default connect(mapStateToProps, { newList }) (List);
In your reducer, you have the following -
allTitles: [...state.allTitles, action.payload.title]
When you do, I don't see title in the redux state.
const mapStateToProps = (state) => {
const { titles } = state
return { titles }
};
You need to do
const mapStateToProps = (state) => {
const { allTitles } = state
return { allTitles }
};
Then do {this.props.allTitles.length} inside the render statement
Getting Redux setup can be pretty tricky in my opinion. After taking a look at your code I created a small React-Native project and setup Redux as closely as possibly to what you described in your question. Hopefully my answer helps. Please note that all three the files in my answer (App.js, Home.js, & titleReducer.js) are contained in the same directory.
App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import titleReducer from './titleReducer';
// React-Redux
import {
createStore,
combineReducers,
} from 'redux';
import {
connect,
Provider
} from 'react-redux';
// Import Components (Screens)
import Home from './Home';
// Intialize Redux Store
const rootReducer = combineReducers({
titles: titleReducer
});
const store = createStore(rootReducer);
class App extends React.Component {
render() {
return (
<Provider store={store}>
<Home/>
</Provider>
)
}
}
export default App;
titleReducer.js
const initialState = {
allTitles: [],
};
const titleReducer = (state, action) => {
// check for state undefined to prevent
// redux from crashing app on load
if (typeof state === 'undefined') {
return {...initialState};
}
switch (action.type) {
case 'ADD_TITLE':
const newState = {...state};
const newTitle = action.payload;
newState.allTitles.push(newTitle);
return newState;
default:
return {...state};
}
// If none of the conditions above are true,
// simply return a copy of the current state
return {...state};
};
export default titleReducer;
Home.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import {
connect,
Provider
} from 'react-redux';
function randomTitle() {
return Math.random().toString();
}
class Home extends React.Component {
render() {
return (
<View>
<Text>Redux Test</Text>
<Button
title="Add Title"
onPress={ () => this.props.addTitle(randomTitle()) }/>
<Text>{this.props.titles.allTitles.length}</Text>
</View>
)
}
}
const mapDispatchToProps = dispatch => {
return {
addTitle: (payload) => dispatch({type: 'ADD_TITLE', payload: payload}),
};
};
const mapStateToProps = (state) => {
return {
titles: state.titles,
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I think you've forgot to define a store for your app. Go to your root class (app.js or something) and define your reducers to your store:
const store = createStore(tagReducer)
or if you have multiple reducers you can combine them in one line:
const store = createStore(combineReducers({
tag: tagReducer,
someOther: otherReducer
}));
Hope that it fixes your problem.

How to make a Search Bar using React Native with Redux

I want to make a search bar for searching items from the list using react native with redux
I have tried this :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Text, View, FlatList, Image, ScrollView, StyleSheet, TouchableOpacity, TextInput} from 'react-native';
import { SearchBox, Spinner } from './common';
import { listShow, searchResult} from './actions';
class flatRedux extends Component {
componentWillMount() {
this.props.listShow();
}
_onSearchChange = text => {
this.props.searchResult(text)
this.props.listShow(text)
}
render() {
console.log(this.props);
return (
<View style={styles.MainContainer}>
<SearchBox
placeholder="Search..."
onChangeText={this._onSearchChange}
/>
<FlatList
data={this.props.flatlist}
ItemSeparatorComponent = {this.FlatListItemSeparator}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) =>
<Text key={item.id} style={styles.FlatListItemStyle} >
{item.cl_name} </Text>}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
search: state.searchResult.search,
flatlist: state.listShow.flatlist
};
};
export default connect(mapStateToProps, { listShow, searchResult })(flatRedux);
This is the SearchAction file
import { SEARCH_DATA } from './types'
export const searchResult = (text) => {
return {
type: SEARCH_DATA,
payload: text
};
}
And this one is SearchReducer File
import {SEARCH_DATA} from "../actions";
const INITIAL_STATE = {
search: ''
}
export default (state = INITIAL_STATE, action) => {
console.log(action);
switch(action.type) {
case SEARCH_DATA:
return {
...state,
search: action.payload
}
default:
return state;
}
}
And files who are fetching items from the localhost server are below :
flatAction.js
import axios from 'axios';
import { FLAT_DATA } from './types';
export const listShow = () => {
return (dispatch) => {
axios.post('http://192.168.48.228/reactTest/list.php')
.then((response) => {
dispatch({
type: FLAT_DATA,
payload: response.data
});
})
.catch((error) => {
console.log(error);
});
};
};
flatReducer.js
import {FLAT_DATA } from '../actions/types';
const INITIAL_STATE = {
flatlist: '',
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FLAT_DATA:
return { ...state, flatlist: action.payload };
default:
return state;
}
};
All items are fetching but 'Search' is not working for them.
'Search' is not working for them?
it's not detailed enough to give a specific answer.
To debug the workflow try changing the endpoint of listShow to search and then calling this.props.listShow(text) on input onChange handler the see if the redux part is properly connected
assuming,
All items are fetching
If it is not working need to see if your keyListners are working or not (need to see the code for searchbox), and finally, if the flatlist is rerendered (It doesn't rerender if shallow comparison fails)

React native axios api calls with redux

I'm struggling to get a basic api call setup with redux and axios in React Native.
This is my reducer index.js
import { combineReducers } from 'redux'
import LibaryReducer from './LibraryReducer'
import ImportLibraryReducer from './ImportLibraryReducer'
let defaultState = {
card: null
}
const mainReducer = (state = defaultState, action) => {
if(action.type === "CHANGE_CARDS") {
return {
...state,
card: action.card
}
} else {
return {
...state
}
}
}
export default mainReducer
This is my action index.js
import axios from "axios"
export function loadCards(){
return(dispatch)=>{
return axios.get('http://localhost:4000/reports')
.then(response => {
dispatch(changeCards(response.data))
})
}
}
export function changeCards(cards) {
return{
type: "CHANGE_CARDS",
card: card
}
}
This is my app.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import MainPage from './components/MainPage'
import { Header } from "native-base"
import Card from './components/Card'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducers from './reducers'
const store = createStore(reducers, applyMiddleware(thunk))
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<Provider store={store}>
<View>
<Header ><Text>hello</Text></Header>
<Card />
</View>
</Provider>
);
}
}
And, finally, this is where I'm trying to retrieve the data from the api call:
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import {Collapse,CollapseHeader, CollapseBody, AccordionList} from 'accordion-collapse-react-native';
import { connect } from 'react-redux'
import * as actions from '../actions'
class Card extends Component {
render() {
const titleStyle = {
backgroundColor: '#edeeef',
fontWeight: "bold",
color: '#454647',
fontSize: 16,
left: 8,
fontFamily: 'Ionicons',
top: 10
}
const descMarkStyle = {
left: 8,
top: 4,
fontFamily: 'Ionicons',
color: '#454647',
fontSize: 16
}
console.log('in the render', this.props)
return (
<View>
<Collapse >
<CollapseHeader>
<View
style={{
backgroundColor: '#edeeef',
height: 38,
postion: 'absolute',
borderBottomWidth: .5,
borderBottomColor: '#black'
}}
>
<Text style={titleStyle}>
test
</Text>
</View>
</CollapseHeader>
<CollapseBody>
<Text style={descMarkStyle}>test</Text>
<Text style={descMarkStyle}>test</Text>
</CollapseBody>
</Collapse>
</View>
);
}
}
function mapStateToProps(state) {
return {
state
};
}
export default connect(mapStateToProps)(Card);
When I try to console log this.props in the component above, I get the default state of card: null without the api running: https://imgur.com/a/acB40KU
I'm new to redux, and I feel like there is something obvious that I'm missing.
You should trigger your action in the componentDidMount lifecycle method in your Card component. Also, you can destructure your actions in your imports and in your connect.
import { loadCards } from '../actions'
class Card extends Component {
componentDidMount() {
this.props.loadCards()
}
And in connect:
export default connect(mapStateToProps, { loadCards })(Card);
Also in the changeCards action:
card: cards
Here is how to set up axios with redux hooks and react-native in 4 steps:
source code: https://github.com/trackmystories/Redux-hooks-counter-app-with-axios.
Step 1:
create an actions.js file:
actions.js
export const TOTAL_COUNT = "TOTAL_COUNT";
export const totalCount = (data) => ({
type: TOTAL_COUNT,
data,
});
Step 2:
define and combine your reducers:
reducer.js
import { combineReducers } from "redux";
import { TOTAL_COUNT } from "./actions";
let dataState = { data: [] };
const total_counts = (state = dataState, action) => {
switch (action.type) {
case TOTAL_COUNT:
return { ...state, data: action.data };
default:
return state;
}
};
const counter = (state = 0, action) => {
switch (action.type) {
case "ADD":
return state + 1;
case "SUBTRACT":
return state - 1;
default:
return state;
}
};
const rootReducer = combineReducers({
counter,
total_counts,
});
export default rootReducer;
Step 3
create an axios get request and put request as defined in the example below and dispatch and get data.
With hooks you don't need to use connect mapStateToProps and dispatchStateToProps with redux hooks instead use { useDispatch, useSelector }.
We can pass the actions "ADD" and "SUBTRACT" inside of the button directly, without defining an action.js file.
CounterComponent.js
import React, { useEffect, useState } from "react";
import { StyleSheet, Text, View, ActivityIndicator } from "react-native";
import ActionButton from "./ActionButton";
import SubmitButton from "./SubmitButton";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { totalCount } from "../actions";
export default function CounterComponent() {
const dispatch = useDispatch();
const [isFetching, setIsFetching] = useState(false);
const total_counts = useSelector((state) => state.total_counts);
const counter = useSelector((state) => state.counter);
const { data } = total_counts;
useEffect(() => getTotalCount(), []);
const getTotalCount = () => {
setIsFetching(true);
let url = "https://url.firebaseio.com<name>.json";
axios
.get(url)
.then((res) => res.data)
.then((data) => dispatch(totalCount(data)))
.catch((error) => alert(error.message))
.finally(() => setIsFetching(false));
};
const onSubmit = (counterState) => {
let url = "https://url.firebaseio.com<name>.json";
axios.put(url, counterState).then((response) => {
console.log(response);
});
};
return (
<View>
<ActionButton
onPress={() =>
dispatch({
type: "SUBTRACT",
})
}
title="subtract"
/>
<View>
{isFetching ? (
<ActivityIndicator />
) : (
<View>
<Text>
Current state:
{data.counter ? data.counter : counter}
</Text>
</View>
)}
</View>
<ActionButton
onPress={() =>
dispatch({
type: "ADD",
})
}
title="add"
/>
<SubmitButton
onPress={onSubmit({
counterState: counter,
})}
title="Submit"
/>
</View>
);
}
Step 4:
Lastly link your RootReducer to the createStore and pass it to the Provider.
import React from "react";
import { Text, View } from "react-native";
import { Provider } from "react-redux";
import { createStore } from "redux";
import CounterComponent from "./src/components/CounterComponent";
import rootReducer from "./src/reducer";
const store = createStore(rootReducer);
export default function App() {
return (
<View>
<Text>Counter example with Redux Hooks and Axios</Text>
<Provider store={store}>
<CounterComponent />
</Provider>
</View>
);
}

Unable to access reducer state in container

My Login Component:
import React, { Component } from 'react'
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity
} from 'react-native'
export class Login extends Component {
onChangeText = (key, value) => {
this.props.setUserDetails({
...this.props.user,
[key]: value
})
}
render() {
const { user, onSubmitForm } = this.props
console.log(this.props.user) // user undefined here
return (
<View style={styles.container}>
<Text style={styles.heading}>Login</Text>
<TextInput
placeholder='Email'
onChangeText={val => this.onChangeText('email', val)}
style={styles.input}
value={user.email}
/>
<TextInput
placeholder='Password'
onChangeText={val => this.onChangeText('password', val)}
style={styles.input}
value={user.password}
/>
<TouchableOpacity onPress={() => onSubmitForm(user)}>
<View style={styles.button}>
<Text style={styles.buttonText}>Submit</Text>
</View>
</TouchableOpacity>
</View>
)
}
}
My Login container:
import React, { Component } from 'react'
import { setUserDetails } from '../actions/loginActions'
import { connect } from 'react-redux'
import loginReducer from '../reducers/loginReducer'
import { Login } from '../components/login'
export class LoginContainer extends Component {
onSubmitForm = () => {
// Checking Validations
const { name, email } = this.props;
if (!name || !email) {
alert('Please fill the form')
console.log(this.props.user) // It says undefined
return;
}
}
render () {
return (
<Login
user={this.props.user}
setUserDetails={this.props.setUserDetails}
onSubmitForm={this.onSubmitForm}
/>
)
}
}
const mapStateToProps = (state) => ({
user: state.loginReducer.user,
})
const mapDispatchToProps = dispatch => ({
setUserDetails: payload => dispatch(setUserDetails(payload)),
})
export default connect(mapStateToProps, mapDispatchToProps)(Login)
My login Reducer:
const initialState = {
user: {
email: '',
password: '',
}
}
const loginReducer = (state = initialState, action) => {
switch(action.type) {
case SET_USER_DETAILS:
return Object.assign({}, state, {
user: action.user
})
default:
return state
}
return state
}
export default loginReducer
My root Reducer:
import { combineReducers } from 'redux';
import loginReducer from './loginReducer'
const rootReducer = combineReducers({
loginReducer
})
export default rootReducer
MY store configuration:
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import rootReducer from './reducers'
const persistConfig = {
key: 'mykey',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer)
const persistedStore = persistStore(store)
export default store
I am learning React native and trying to implement some features.
The problem is that I just can't access my this.props.user in the Login container when the submit is called. What am I missing in this scenario?
Any help is appreciated.
I've noticed some weird thing. Your default export of LoginContainer.js is connected Login component. I guess what you really meant is instead of this:
// ...imports
export class LoginContainer extends Component {
// ...
}
//...
export default connect(mapStateToProps, mapDispatchToProps)(Login)
to use this:
// ...imports
// no need to 'export class ...' here.
class LoginContainer extends Component {
// ...
}
// ...
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)
In the container you used:
const mapStateToProps = (state) => ({
user: state.loginReducer.user,
})
But in your initial state there is not loginReducer. Maibe it works:
const mapStateToProps = (state) => ({
user: state.user
})

React Native : Actions must be plain objects. Use Custom Middlewares for async operations

I know this is common issue and have been asked many times but i have gone through every solution but it didn't work.
I have been facing this error when i try to login from login form.
Here's code i'm attaching.
Login.js (View)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ScrollView, Text, TextInput, View, Button,StyleSheet,TouchableOpacity } from 'react-native';
import {loginRequest} from './../redux/actions/auth';
import {bindActionCreators} from 'redux';
class Login extends Component {
constructor (props) {
super(props);
this.state = {
username: '',
password: ''
};
}
userLogin (e) {
this.props.actions.loginRequest(this.state.username, this.state.password);
}
render () {
return (
<ScrollView style={{padding: 20,backgroundColor:'#ccc'}}>
<View style = {styles.container}>
<View style={{marginLeft:15}}>
<Text>Email</Text>
</View>
<TextInput
style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Enter username"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText={(text) => this.setState({ username: text })}/>
<View style={{marginLeft:15}}>
<Text>Password</Text>
</View>
<TextInput
style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Enter Password > 6 letters"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText={(text) => this.setState({ password: text })}/>
<TouchableOpacity
style = {styles.submitButton}
onPress={(e) => this.userLogin(e)}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
</ScrollView>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.auth.isLoggedIn
};
}
function mapDispatchToProps(dispatch){
return {
actions : bindActionCreators({
loginRequest
},dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
auth action
In this action i am calling dispatch without any api call directly just for testing but even though that's not working.
export function loginRequest(username,password) {
alert("TEst"); // this alert comes
return function (dispatch) {
alert(`........Tracker......`); // execution doesn't reach here ,this alert doesn't com
if(username == 'admin' && password == 'admin'){
alert(`Login Success......`);
dispatch({
type : 'LOGIN_SUCCESS',
msg : 'Logged in successfully.'
});
resolve(true);
} else {
alert(`Login Failed......`);
dispatch({
type : 'LOGIN_FAIL',
msg : 'Please make sure you have entered valid credentials.'
})
reject(false);
}
};
}
This is my authReducer.js
export default function reducer(state = {},action){
if(action.type == 'LOGIN_SUCCESS'){
alert('login success');
return Object.assign({},state,{
isLoggedIn:true
})
} else if(action.type == 'LOGIN_FAIL'){
alert('login failed');
return Object.assign({},state,{
isLoggedIn:false
})
} else {
return state;
}
}
And entry point
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
import {Provider} from 'react-redux';
import store from './redux';
import Application from './pages/Application';
export default class App extends Component{
render() {
return (
<Provider store={store}>
<Application />
</Provider>
);
}
}
I couldn't find any solution, can anyone help?
Because you are returning a function from your actions, you need to use middleware to handle returning functions instead of objects.
I am a fan of redux-thunk but there are plenty of other redux middlewares out there for this exact purpose.
You will need to update your store and configure it to use the middleware like so:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
export default(initialState) => {
return createStore(rootReducer, initialState, applyMiddleware(thunk));
}

Resources