Search code examples
reactjsreduxreact-reduxredux-thunk

Default value for Redux-thunk input


I have a simple SPA with thunk. It uses github API to get a list of repos. I had previously an example with class presentational component. It had a local state, but I decided to simplify example as much as possible and refactored it to function and removed local state and using ref to get input value. It works fine

  1. How to set the default value in input field, so that when the app loads it gets that value.
  2. I don't quite understand how to remove combineReducers and use a single reducer as when I use createStore with single reducer app breaks

Here is the code: https://codesandbox.io/s/k13nowrj33

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { applyMiddleware, combineReducers, createStore } from "redux";
import { connect, Provider } from "react-redux";
import thunk from "redux-thunk";
import "./index.css";

// actions.js
const addRepos = repos => ({ type: "ADD_REPOS", repos });
const clearRepos = () => ({ type: "CLEAR_REPOS" });
const getRepos = username => async dispatch => {
  try {
    const url = `https://api.github.com/users/${username}/repos`;
    const response = await fetch(url);
    const responseBody = await response.json();
    dispatch(addRepos(responseBody));
  } catch (error) {
    console.log(error);
    dispatch(clearRepos());
  }
};

// reducers.js
const repos = (state = [], action) => {
  switch (action.type) {
    case "ADD_REPOS":
      return action.repos;
    case "CLEAR_REPOS":
      return [];
    default:
      return state;
  }
};

const rootreducer = combineReducers({ repos });

const store = createStore(rootreducer, applyMiddleware(thunk));

// App.js
function App(props) {
  var textInput;
  var setTextInputRef = element => { textInput = element; };
  var submit = () => props.getRepos(textInput.value);
  return (
    <div>
      <h1>Github username: </h1>
      <input type="text" ref={setTextInputRef} />
      <button onClick={submit}>Get All Repos</button>
      <ul>
        {props.repos.map((repo, index) => (<li key={index}><a href={repo.html_url}>{repo.name}</a></li> ))}
      </ul>
    </div>
  );
}

// AppContainer.js
const mapStateToProps = state => ({ repos: state.repos });
const mapDispatchToProps = { getRepos };
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);

ReactDOM.render(<Provider store={store}><AppContainer /></Provider>, document.getElementById("root"));


Solution

  • 1.) You can use defaultValue for this purpose.

    2.) As mentioned in the comments, if you don't use combineReducers() you'll need to change your mapStateToProps().

    Here's one way to do it:

    import React, { Component } from "react";
    import ReactDOM from "react-dom";
    import { applyMiddleware, combineReducers, createStore } from "redux";
    import { connect, Provider } from "react-redux";
    import thunk from "redux-thunk";
    import "./index.css";
    
    // actions.js
    const addRepos = repos => ({ type: "ADD_REPOS", repos });
    const clearRepos = () => ({ type: "CLEAR_REPOS" });
    const getRepos = username => async dispatch => {
      try {
        const url = `https://api.github.com/users/${username}/repos`;
        const response = await fetch(url);
        const responseBody = await response.json();
        dispatch(addRepos(responseBody));
      } catch (error) {
        console.log(error);
        dispatch(clearRepos());
      }
    };
    
    // reducers.js
    const repos = (state = [], action) => {
      switch (action.type) {
        case "ADD_REPOS":
          return action.repos;
        case "CLEAR_REPOS":
          return [];
        default:
          return state;
      }
    };
    
    const store = createStore(repos, applyMiddleware(thunk));
    
    // App.js
    function App(props) {
      var textInput;
      var setTextInputRef = element => {
        textInput = element;
      };
      var submit = () => props.getRepos(textInput.value);
      return (
        <div>
          <h1>Github username: </h1>
          <input defaultValue="colinricardo" type="text" ref={setTextInputRef} />
          <button onClick={submit}>Get All Repos</button>
          <ul>
            {props.repos.map((repo, index) => (
              <li key={index}>
                <a href={repo.html_url}>{repo.name}</a>
              </li>
            ))}
          </ul>
        </div>
      );
    }
    
    // AppContainer.js
    const mapStateToProps = state => ({ repos: state });
    
    const mapDispatchToProps = { getRepos };
    const AppContainer = connect(
      mapStateToProps,
      mapDispatchToProps
    )(App);
    
    ReactDOM.render(
      <Provider store={store}>
        <AppContainer />
      </Provider>,
      document.getElementById("root")
    );
    

    CodeSandbox here.

    To get repos on load:

    import React, { Component } from "react";
    import ReactDOM from "react-dom";
    import { applyMiddleware, combineReducers, createStore } from "redux";
    import { connect, Provider } from "react-redux";
    import thunk from "redux-thunk";
    import "./index.css";
    
    // actions.js
    const addRepos = repos => ({ type: "ADD_REPOS", repos });
    const clearRepos = () => ({ type: "CLEAR_REPOS" });
    const getRepos = username => async dispatch => {
      try {
        const url = `https://api.github.com/users/${username}/repos`;
        const response = await fetch(url);
        const responseBody = await response.json();
        dispatch(addRepos(responseBody));
      } catch (error) {
        console.log(error);
        dispatch(clearRepos());
      }
    };
    
    // reducers.js
    const repos = (state = [], action) => {
      switch (action.type) {
        case "ADD_REPOS":
          return action.repos;
        case "CLEAR_REPOS":
          return [];
        default:
          return state;
      }
    };
    
    const store = createStore(repos, applyMiddleware(thunk));
    
    // App.js
    class App extends React.Component {
      componentDidMount() {
        this.submit();
      }
    
      submit = () => this.props.getRepos(this.textInput.value);
    
      render() {
        return (
          <div>
            <h1>Github username: </h1>
            <input
              defaultValue="colinricardo"
              type="text"
              ref={ref => (this.textInput = ref)}
            />
            <button onClick={this.submit}>Get All Repos</button>
            <ul>
              {this.props.repos.map((repo, index) => (
                <li key={index}>
                  <a href={repo.html_url}>{repo.name}</a>
                </li>
              ))}
            </ul>
          </div>
        );
      }
    }
    // AppContainer.js
    const mapStateToProps = state => ({ repos: state });
    
    const mapDispatchToProps = { getRepos };
    const AppContainer = connect(
      mapStateToProps,
      mapDispatchToProps
    )(App);
    
    ReactDOM.render(
      <Provider store={store}>
        <AppContainer />
      </Provider>,
      document.getElementById("root")
    );