I am integrating redux with my react-native app. I have moved my state and action management to Container and integrated the container with component using 'connect'.
App.js
const AppNavigator = createSwitchNavigator({
SplashScreen: SplashScreen,
render() {
return(
<Provider store={store}>
<AppNavigator/>
</Provider>
)
}
});
const store = createStore(reducer);
export default createAppContainer(AppNavigator);
SignIn.js
import React from "react";
import {View} from "react-native";
import authenticateUser from "../../../services/api/authenticateUser";
const SignIn = (props) => {
const authenticate = async () => {
try {
return await authenticateUser.get('/abc', {
params: {
code,
}
});
}
catch (e) {
}
}
const validateUserCredentials = (isValid) => {
authenticate().then(response => {
const responseData = response.data;
props.updateEventRules(responseData);
});
}
}
return (
<View></View>
);
export default SignIn;
Sign-InContainer.js
import {eventRulesUpdated} from '../../../actions/actions';
import {connect} from 'react-redux';
import SignIn from './signin-screen';
const mapStateToProps = (state) => ({});
const mapDispatchToProps = dispatch => {
return {
updateEventRules: rules => {
dispatch(eventRulesUpdated(rules))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
When running the app I am getting an error that - props.updateEventRules() is not a function.
Can anyone please help me what am I doing wrong?
You should have the connect functions inside Signin.js like this
import React from "react";
import {View} from "react-native";
import authenticateUser from "../../../services/api/authenticateUser";
const SignIn = (props) => {
const authenticate = async () => {
try {
return await authenticateUser.get('/abc', {
params: {
code,
}
});
}
catch (e) {
}
}
const validateUserCredentials = (isValid) => {
authenticate().then(response => {
const responseData = response.data;
props.updateEventRules(responseData);
});
}
}
return (
<View></View>
);
const mapStateToProps = (state) => ({});
const mapDispatchToProps = dispatch => {
return {
updateEventRules: rules => {
dispatch(eventRulesUpdated(rules))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
Related
The code below is not working. It doesn't see the apikey in parentheses.I couldn't understand what I had to do here. Is it a problem with the hooks structure?
import React, { useEffect } from 'react';
import MovieListing from "../MovieListing/MovieListing";
import movieApi from "../../common/apis/movieApi";
import { APIKey } from "../../common/apis/MovieApiKey"; //the program does not see this
import "./Home.scss";
import { useDispatch } from 'react-redux';
import { addMovies } from '../../features/movies/movieSlice';
const Home = () => {
const dispatch = useDispatch();
useEffect(() => {
const movieText = "Harry";
const fetchMovies = async () => {
const response = await movieApi.get('?apiKey=${APIKey}&s=${movieText}&type=movie')
.catch((err) => { console.log("Err :", err) });
dispatch(addMovies)(response.data); //api key does not see this
};
fetchMovies();
}, []);
return (
<div>
<div className='banner-img'></div>
<MovieListing />
</div>
);
};
export default Home;
I have just completed Learn Redux on Codecademy and want to that knowledge in practice. But I have an error. When I create extraReducers for updating the state to actual promise status it does not add information.
getUserSlice.js
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchUserInfo } from '../../api';
export const loadUser = createAsyncThunk("getUser/loadUser",
async (arg, thunkAPI) => {
return await fetchUserInfo();
}
});
const sliceOptions = {
name: 'getUser',
initialState: {
info: [],
isLoading: false,
hasError: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadUser.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(loadUser.fulfilled, (state, action) => {
state.info.push(action.payload)
state.isLoading = false;
state.hasError = false;
})
.addCase(loadUser.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
})
},
};
export const getUserSlice = createSlice(sliceOptions);
console.log(getUserSlice);
export const selectUserInfo = (state) => {
console.log(state);
return state;
};
export default getUserSlice.reducer;
api.js
export const fetchUserInfo = async () => {
const user = await fetch('http://localhost:5000/api/user');
const json = user.json();
return json;
}
App.js
import React from 'react';
import './App.css';
import {Container} from 'react-bootstrap';
import Achievement from './components/Achievement/Achievement';
import { useSelector } from 'react-redux';
import { selectUserInfo } from './features/getUser/getUserSlice';
const colors = ['#010626','#4d6396', '#5d1a87', '#5d1a87', '#5d1a87'];
function App() {
let color= colors[0];
const user = useSelector(selectUserInfo)
function changeColor() {
const newColor = `rgb(${Math.round(Math.random() *256)}, ${Math.round(Math.random() *256)}, ${Math.round(Math.random() *256)})`;
color = newColor;
}
return (
<div className="App" style={{ background: color }}>
<Container>
<h1 id="whoAmI">
Witaj na moim portfolio
{user}
</h1>
<button onClick={changeColor}>
Zmień kolor tła
</button>
<div className="col-lg-4 col-md-6 col-sm-12">
<Achievement title="Ukończenie The Web Developer Bootcamp" picture="https://res.cloudinary.com/syberiancats/image/upload/v1630317595/k9g0nox2fyexxawg8whu.jpg" />
</div>
</Container>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import reportWebVitals from './reportWebVitals';
import { store } from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
reportWebVitals();
store.js
import { configureStore } from "#reduxjs/toolkit";
import getUserReducer from "./features/getUser/getUserSlice";
export const store = configureStore({
reducer: {
getUser: getUserReducer
}
})
Console.log of getUserSlice and state in the selector
Maybe you can use (builder) => {} function in extraReducer and you edit your loadUser because your Api.js already return json like code below:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchUserInfo } from '../../api';
export const loadUser = createAsyncThunk('getUser/loadUser', async (arg, thunkAPI) => {
return await fetchUserInfo();
});
const sliceOptions = {
name: 'getUser',
initialState: {
info: [],
isLoading: false,
hasError: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadUser.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(loadUser.fulfilled, (state, action) => {
state.info.push(action.payload)
state.isLoading = false;
state.hasError = false;
})
.addCase(loadUser.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
})
},
};
export const getUserSlice = createSlice(sliceOptions);
console.log(getUserSlice);
export const selectUserInfo = (state) => {
console.log(state);
return state;
};
export default getUserSlice.reducer;
and you maybe forgot to add await before fetch, you should edit your fetching data in api.js into this below:
export const fetchUserInfo = async () => {
const user = await fetch('http://localhost:5000/api/user');
const json = user.json();
return json;
}
You can implicitly return and bypass the Promise result. Something like:
(arg, thunkAPI) => fetchUserInfo();
However, I would take the "by-the-book" way:
export const loadUser = createAsyncThunk("getUser/loadUser",
async (arg, tunkApi) => {
try {
const response = await fetchUserInfo();
return response;
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}
});
I am developing a mobile app which makes GET calls using fetch api. I am stuck in that I am trying to export json object( fetched from server with fetch method) to another .js file to be used as array, But when I import my function in another.js (below), it returns nothing. I tested my fetch method with console so it works as expected, however I am unable to process data in another.js file. By the way, I have searched a lot and found this post Helpful, but not worked.
Below code is implementation of fetch part and exporting it.(Products.js)
import React, { PureComponent,Component } from "react";
import { connect } from "react-redux";
import { View } from "react-native";
import { productsDataSelector } from "../../Products/ProductsSelectors";
import ProductsList from "../../ProductsList/ProductsList";
import Product from "../../Product/Product";
import { NavigationActions, StackActions } from "react-navigation";
import AnotherComponent from "../../Products/ProductsReducer";
class Products extends PureComponent {
render() {
const { navigation } = this.props;
const { productsData } = this.props;
return (
<View>
<ProductsList list={productsData} isSortable>
{product => <Product product={product} />}
</ProductsList>
</View>
);
}
}
const mapStateToProps = state => ({
productsData: productsDataSelector(state)
});
export const getMoviesFromApiAsync = () =>
fetch('http://localhost:8080/JweSecurityExample/rest/security/retrieveItems')
.then((response) => response.json())
export default connect(
mapStateToProps,
null
) (Products);
Below code is another.js where importing fetch function and processing returning json object without class declaration implemented.
import React, { Component } from "react";
import {getMoviesFromApiAsyncc} from "../screens/Products/Products";
const fakeData = [];
export const someFunc = () => {
fetch('http://localhost:8080/JweSecurityExample/rest/security/retrieveItems')
.then((response) => response.json())
.then((responseJson) => console.log("responsee:"+JSON.stringify(responseJson)))
.then((responseJson) => {fakeData:JSON.stringify(responseJson)})
.catch((error) => {
console.error(error);
});
};
someFunc();
const initialState = {
data:this.fakeData
};
export default (state = initialState,action) => {
return state;
};
Any recommendations ?? Thanx
I don't see where in your code do you call someFunc and one more thing you need to wrap the object that you return from someFunc in braces otherwise it will be treated as the function's body.
export const someFunc = () => {
getMoviesFromApiAsync().then(response => {
fakeData = JSON.stringify(response)
})
};
someFunc();
I suggest that you move getMoviesFromApiAsync to a separate file and call it from your component to get the list of movies.
api.js
export const getMoviesFromApiAsync = () =>
fetch('http://localhost:8080/JweSecurityExample/rest/security/retrieveItems')
.then((response) => response.json());
product.js
import React, { PureComponent,Component } from "react";
import { connect } from "react-redux";
import { View } from "react-native";
import { productsDataSelector } from "../../Products/ProductsSelectors";
import ProductsList from "../../ProductsList/ProductsList";
import Product from "../../Product/Product";
import { NavigationActions, StackActions } from "react-navigation";
import AnotherComponent from "../../Products/ProductsReducer";
// import getMoviesFromApiAsync
import { getMoviesFromApiAsync } from 'PATH_TO_API.JS'
class Products extends Component {
async componentDidMount(){
const list = await getMoviesFromApiAsync();
console.log(list);
}
render() {
const { navigation } = this.props;
const { productsData } = this.props;
return (
<View>
<ProductsList list={productsData} isSortable>
{product => <Product product={product} />}
</ProductsList>
</View>
);
}
}
This issue likely stems from a misconfiguration of redux-thunk or a misunderstanding of how to write a thunk. I've tried a lot of different ways, but from what I can tell, this should work. However, I'm still getting a console message that says its firing a redux action of undefined.
Here is my store configuration
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import App from './components/App';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById('rootElement')
);
Here is my action:
import axios from 'axios';
export const GET_ABOUT_CONTENT_REQUEST = 'GET_ABOUT_CONTENT_REQUEST';
export const GET_ABOUT_CONTENT_FAILED = 'GET_ABOUT_CONTENT_FAILED';
export const GET_ABOUT_CONTENT_OK = 'GET_ABOUT_CONTENT_OK';
export const fetchAboutContent = () => {
const url = `http://localhost:3000/about`;
return (dispatch, getState) => {
if (getState.isInitialized === true){
console.log("desktop init should not be called when already desktop is init")
return Promise.resolve();
}
if (getState.about.isLoading) {
console.log('is loading');
return Promise.resolve();
}
dispatch({ type: GET_ABOUT_CONTENT_REQUEST });
axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
}
}
Here is me firing the action in my component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../../actions/about';
import getAboutContent from '../../reducers';
class AboutMe extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.getAboutContent();
}
render() {
return <div>{ this.props.content }</div>
}
}
const mapStateToProps = (state) => ({
content: {} || getAboutContent(state)
})
const mapDispatchToProps = (dispatch) =>
bindActionCreators({ getAboutContent }, dispatch)
export default connect(
mapStateToProps, mapDispatchToProps
)(AboutMe);
I've tried quite a few configurations for mapDispatchToProps, i.e. connect(..., { fetchData: getAboutContent })..., and more. Any help is greatly appreciated.
Edit:
Here is the git repo, if that is helpful to anybody: https://github.com/sambigelow44/portfolio-page
Check your reducer name,you export fetchAboutContent, but import getAboutContent.
Code in action file is seems to be incorrect.
getState is a function.
const state = getState();
Change below code.
import axios from 'axios';
export const GET_ABOUT_CONTENT_REQUEST = 'GET_ABOUT_CONTENT_REQUEST';
export const GET_ABOUT_CONTENT_FAILED = 'GET_ABOUT_CONTENT_FAILED';
export const GET_ABOUT_CONTENT_OK = 'GET_ABOUT_CONTENT_OK';
export const fetchAboutContent = () => {
const url = `http://localhost:3000/about`;
return (dispatch, getState) => {
if (getState().isInitialized === true){
console.log("desktop init should not be called when already desktop is init")
return Promise.resolve();
}
if (getState().about.isLoading) {
console.log('is loading');
return Promise.resolve();
}
dispatch({ type: GET_ABOUT_CONTENT_REQUEST });
axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
}
}
Also you need to return promise from axios call, just add return statement.
return axios.get(url)
.then(res => dispatch({
type: GET_ABOUT_CONTENT_OK,
res
}))
.error(err => dispatch({
type: GET_ABOUT_CONTENT_FAILED,
err
}));
I'm new with Redux Thunk and I'm having problems with dispatch an action after fetching async call by click on button component.
actions.js
import fetch from 'isomorphic-fetch'
export const getPosts = (json) => {
return {
type: constant.GET_POSTS,
payload: {
data: json
}
}
}
export const loadPosts () => {
return (dispatch) => {
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
res.json()
}).then(json => {
dispatch(getPosts(json))
})
}
}
button.js
class Button extends React.Component {
clicked(){
console.log(this.props.loadJsonPosts()) // got undefined here
}
render() {
return(
<button onClick={this.clicked.bind(this)}>click</button>
)
}
}
buttonContainer.js
import connect from 'react-redux/lib/components/connect'
import { loadPosts } from '../actions/actions.js'
import Button from '../components/Button'
function mapDispatchToProps(dispatch) {
return {
loadJsonPosts: () => { dispatch(loadPosts()) }
}
}
export default connect(null, mapDispatchToProps)(Button)
reducer.js
import * as constant from '../constants/index'
let initialState = { postList: [] }
const reducer = (state = initialState, action) => {
switch (action.type) {
case constant.GET_POSTS: //here i call my loadPosts action
state = Object.assign({}, { postList: [{ post: action.data }] })
break;
default:
break;
}
return state
}
export default reducer
App.jsx
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Main from './components/Main'
import thunk from 'redux-thunk'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import reducer from './reducers/reducer'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
class App extends Component {
render() {
return(
<Provider store={store}>
<Main />
</Provider>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
I can't figure out why i get undefined, maybe I've missed something or I've wrong the approach
You forgot to return res.json() in actions.js for the next then block.
it should be
export const loadPosts () => {
return (dispatch) => {
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return res.json();
}).then(json => {
dispatch(getPosts(json))
})
}}
or you can skip the return statement by removing the blocks by writing .then(res => res.json())
I the same issue and found that ensuring the thunk middleware was first in my chain when creating the redux store allowed me to access the promise I was after rather than getting undefined,
store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk, otherMiddleware1, otherMiddleware2)
);
mapDispatchToProps should be like this:
function mapDispatchToProps(dispatch) {
return {
// loadPosts instead of loadPosts()
loadJsonPosts: () => { dispatch(loadPosts) }
} }