I created a very simple app with only React and now I want to change it so, I can use Redux in it. (I know Redux is not needed here, but I'm just trying to learn Redux).
SearchBar.js - Only React
import React, { Component } from "react";
class SearchBar extends Component {
state = {
inputValue: ""
};
handleFormSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state.inputValue);
};
render() {
return (
<div className="header">
<h1>Search for images on Unsplash</h1>
<form onSubmit={this.handleFormSubmit} className="ui form">
<input
type="text"
placeholder="Type here to search for images"
value={this.state.inputValue}
onChange={e => this.setState({ inputValue: e.target.value })}
/>
</form>
</div>
);
}
}
export default SearchBar;
App.js - Only React
import React, { Component } from "react";
import axios from "axios";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
class App extends Component {
state = {
images: []
};
onSearchSubmit = async inputValue => {
const API_KEY =
"<MY API KEY FOR UNSPLASH>";
const response = await axios.get(
`https://api.unsplash.com/search/photos?page=1&query=${inputValue}&client_id=${API_KEY}`
);
this.setState({ images: response.data.results });
};
render() {
return (
<>
<SearchBar onSubmit={this.onSearchSubmit} />
<div>
<ImageList images={this.state.images} />
</div>
</>
);
}
}
export default App;
I put the redux-version on codeSandBox. Of course it's not working yet.
Here are my changes so far:
App.js with redux
import React, { Component } from "react";
import { Provider } from "react-redux";
import store from "./store";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
import "./app.scss";
class App extends Component {
render() {
return (
<Provider store={store}>
<SearchBar onSubmit={this.onSearchSubmit} />
<div>
<ImageList images={this.state.images} />
</div>
</Provider>
);
}
}
export default App;
store.js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
fetchAction.js
import axios from "axios";
export const FETCH_DATA = "fetch_data";
// Getting all images
export const getImages = inputValue => async dispatch => {
const API_KEY =
"<MY API KEY FOR UNSPLASH>";
const res = await axios.get(
`https://api.unsplash.com/search/photos?page=1&query=${inputValue}&client_id=${API_KEY}`
);
console.log(res.data.results);
dispatch({
type: FETCH_DATA,
payload: res.data.results
});
};
index.js inside reducers folder
import { combineReducers } from "redux";
import fetchReducer from "./fetchReducer";
export default combineReducers({
images: fetchReducer
});
fetchReducer.js
import { FETCH_DATA } from "../actions/fetchAction";
const initialState = {};
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_DATA:
return {
...state
};
default:
return state;
}
}
But, I have two questions:
If I add the following to my component, where I use connect:
const mapStateToProps = state => ({ images: });
export default connect( mapStateToProps, { getImages } )(SearchBar);
What would be the value of images inside mapStateToProps?
I put the redux-version on codeSandBox. Of course it's not working yet.
答案 0 :(得分:0)
建议的标准/常见过程是在单独的容器文件中设置连接的逻辑。这将包含您的连接函数以及所有mapStateToProps,mapDispatchToProps等
您的容器将如下所示:
import SearchBar from "./components/SearchBar"
const mapStateToProps = state => ({
images: images(state)
});
export default connect(mapStateToProps)(SearchBar)
答案 1 :(得分:0)
1)您应该在 SearchBar 中使用connect
2)这取决于您调用API调用以获取图像的位置。
尝试这样的操作,您很有可能必须在Gallery组件内调用this.props.getImages(..)
,很可能是在您键入内容
import React, { Component } from "react";
import { connect } from "react-redux";
import { getImages } from "../actions/fetchAction";
import Masonry from "react-masonry-component";
const masonryOptions = {
transitionDuration: 1
};
const imagesLoadedOptions = { background: ".my-bg-image-el" };
class Gallery extends Component {
childElements = () => {
if (this.props.images) {
return this.props.images.map(item => {
return (
<li className="masonry_item" key={item.id}>
<img
src={item.urls.regular}
alt={item.description}
className="masonry_item_img"
/>
</li>
);
});
}
return <div>no images</div>;
};
render() {
console.log(this.props);
// map method generates a new Array
return (
<Masonry
className={"masonry"} // default ''
elementType={"ul"} // default 'div'
options={masonryOptions} // default {}
disableImagesLoaded={false} // default false
updateOnEachImageLoad={false} // default false and works only if disableImagesLoaded is false
imagesLoadedOptions={imagesLoadedOptions} // default {}
>
{this.childElements()}
</Masonry>
);
}
}
let mapStateToProps = (state, props) => {
return {
images: state.images.images
};
};
let mapDispatchToProps = dispatch => {
return {
getImages: data => {
dispatch(getImages(data));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Gallery);
答案 2 :(得分:0)
对于您的store.js
,而不是:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
尝试:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';
import App from "./components/App";
import reducers from "./reducers";
const store = createStore(reducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
);
然后在您的App.js
文件中,而不是:
import React, { Component } from "react";
import { Provider } from "react-redux";
import store from "./store";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
import "./app.scss";
class App extends Component {
render() {
return (
<Provider store={store}>
<SearchBar onSubmit={this.onSearchSubmit} />
<div>
<ImageList images={this.state.images} />
</div>
</Provider>
);
}
}
export default App;
尝试:
import React, { Component } from "react";
import SearchBar from "./components/SearchBar";
import ImageList from "./components/ImageList";
import "./app.scss";
class App extends Component {
render() {
return (
<div>
<SearchBar onSubmit={this.onSearchSubmit} />
<div>
<ImageList images={this.state.images} />
</div>
</div>
);
}
}
export default App;
对于您的Axios请求,不要将所有代码都扔到fetchActions.js
内,而要创建一个文件夹/文件结构apis/unsplash.js
:
import axios from 'axios';
export default axios.create({
baseURL: 'https://api.unsplash.com'
});
然后在您的fetchActions.js
中:
export const FETCH_DATA = "fetch_data";
// Getting all images
export const getImages = inputValue => async dispatch => {
const API_KEY =
"<MY API KEY FOR UNSPLASH>";
const res = await unsplash.get(
`/search/photos?page=1&query=${inputValue}&client_id=${API_KEY}`
);
console.log(res.data.results);
dispatch({
type: "FETCH_DATA",
payload: res.data.results
});
};
您的combineReducers
看起来不错。
您的fetchReducer.js
,我经常在商业应用中看到:const initialState = {};
这并不是真正必要,只需将initialState
中间人删掉即可:
import { FETCH_DATA } from "../actions/fetchAction";
export default function(state = {}, action) {
switch (action.type) {
case "FETCH_DATA":
return {
...state
};
default:
return state;
}
}
漂亮,干净,优雅。现在,在哪里使用连接?在App
或SearchBar
组件中?让我们问问自己,connect
函数的作用是什么?
让我们看一下,我们创建了Redux存储,然后将其传递给Provider
,为什么这样做呢?嗯,是的,这样我们应用程序内的任何组件都可以通过Provider
标签访问Redux存储。
换句话说,访问一些数据。那么什么组件需要访问一些数据? App
?不,不是,它是层次结构中的父组件,只是将所有其他组件保持良好的连线,对吧?
但是我们的SearchBar
将要通过该搜索栏访问一些数据,对吧?
现在,SearchBar
组件可能不是您可能需要connect
函数的唯一组件,但是我将从导入开始:
import { connect } from 'react-redux';
在SearchBar
的顶部,然后在SearchBar
的底部,我将实现:
export default connect()(SearchBar)
到底是什么?为什么我们在SearchBar
周围加上第二组括号?!
正如我曾试图向一位以前的学生解释的那样,该学生向管理员抱怨说我没有能力提供上面看到的那行代码,所以这行代码与执行此操作没有什么不同:
function connect() {
return function() {
return 'howdy!';
}
}
connect();
哦,我的一个函数,它返回一个函数,但是等等,它什么都不会打印出来!
但是如果我立即加上第二个括号:
function connect() {
return function() {
return 'howdy!';
}
}
connect()();
我得到了howdy!
的打印件
我们在这里所做的就是返回一个函数,当我们调用要返回的函数时,我们将第二组括号放在其后,因此第二组括号将调用所返回的函数。
关于您的mapStateToProps
,这里有些遗漏,我认为应该像这样:
const mapStateToProps = state => {
return { images: state.images };
};